< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.LiveTvController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/LiveTvController.cs
Line coverage
8%
Covered lines: 25
Uncovered lines: 278
Coverable lines: 303
Total lines: 1156
Line coverage: 8.2%
Branch coverage
0%
Covered branches: 0
Total branches: 58
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 21.8% (26/119) Branch coverage: 0% (0/20) Total lines: 11994/19/2026 - 12:14:27 AM Line coverage: 8.7% (27/309) Branch coverage: 0% (0/58) Total lines: 11994/30/2026 - 12:14:58 AM Line coverage: 8.7% (27/309) Branch coverage: 0% (0/58) Total lines: 11875/6/2026 - 12:15:23 AM Line coverage: 8.2% (25/303) Branch coverage: 0% (0/58) Total lines: 1156 1/23/2026 - 12:11:06 AM Line coverage: 21.8% (26/119) Branch coverage: 0% (0/20) Total lines: 11994/19/2026 - 12:14:27 AM Line coverage: 8.7% (27/309) Branch coverage: 0% (0/58) Total lines: 11994/30/2026 - 12:14:58 AM Line coverage: 8.7% (27/309) Branch coverage: 0% (0/58) Total lines: 11875/6/2026 - 12:15:23 AM Line coverage: 8.2% (25/303) Branch coverage: 0% (0/58) Total lines: 1156

Coverage delta

Coverage delta 14 -14

Metrics

File(s)

/srv/git/jellyfin/Jellyfin.Api/Controllers/LiveTvController.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel.DataAnnotations;
 4using System.Diagnostics.CodeAnalysis;
 5using System.Linq;
 6using System.Net.Mime;
 7using System.Security.Cryptography;
 8using System.Text;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using Jellyfin.Api.Attributes;
 12using Jellyfin.Api.Extensions;
 13using Jellyfin.Api.Helpers;
 14using Jellyfin.Api.ModelBinders;
 15using Jellyfin.Api.Models.LiveTvDtos;
 16using Jellyfin.Data.Enums;
 17using Jellyfin.Database.Implementations.Enums;
 18using Jellyfin.Extensions;
 19using MediaBrowser.Common.Api;
 20using MediaBrowser.Controller.Dto;
 21using MediaBrowser.Controller.Entities;
 22using MediaBrowser.Controller.Entities.TV;
 23using MediaBrowser.Controller.Library;
 24using MediaBrowser.Controller.LiveTv;
 25using MediaBrowser.Controller.MediaEncoding;
 26using MediaBrowser.Controller.Streaming;
 27using MediaBrowser.Model.Dto;
 28using MediaBrowser.Model.Entities;
 29using MediaBrowser.Model.LiveTv;
 30using MediaBrowser.Model.Net;
 31using MediaBrowser.Model.Querying;
 32using Microsoft.AspNetCore.Authorization;
 33using Microsoft.AspNetCore.Http;
 34using Microsoft.AspNetCore.Mvc;
 35
 36namespace Jellyfin.Api.Controllers;
 37
 38/// <summary>
 39/// Live tv controller.
 40/// </summary>
 41public class LiveTvController : BaseJellyfinApiController
 42{
 43    private readonly ILiveTvManager _liveTvManager;
 44    private readonly IGuideManager _guideManager;
 45    private readonly ITunerHostManager _tunerHostManager;
 46    private readonly IListingsManager _listingsManager;
 47    private readonly IRecordingsManager _recordingsManager;
 48    private readonly IUserManager _userManager;
 49    private readonly ILibraryManager _libraryManager;
 50    private readonly IDtoService _dtoService;
 51    private readonly IMediaSourceManager _mediaSourceManager;
 52    private readonly ITranscodeManager _transcodeManager;
 53    private readonly ISchedulesDirectService _schedulesDirectService;
 54
 55    /// <summary>
 56    /// Initializes a new instance of the <see cref="LiveTvController"/> class.
 57    /// </summary>
 58    /// <param name="liveTvManager">Instance of the <see cref="ILiveTvManager"/> interface.</param>
 59    /// <param name="guideManager">Instance of the <see cref="IGuideManager"/> interface.</param>
 60    /// <param name="tunerHostManager">Instance of the <see cref="ITunerHostManager"/> interface.</param>
 61    /// <param name="listingsManager">Instance of the <see cref="IListingsManager"/> interface.</param>
 62    /// <param name="recordingsManager">Instance of the <see cref="IRecordingsManager"/> interface.</param>
 63    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 64    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 65    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 66    /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
 67    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
 68    /// <param name="schedulesDirectService">Instance of the <see cref="ISchedulesDirectService"/> interface.</param>
 369    public LiveTvController(
 370        ILiveTvManager liveTvManager,
 371        IGuideManager guideManager,
 372        ITunerHostManager tunerHostManager,
 373        IListingsManager listingsManager,
 374        IRecordingsManager recordingsManager,
 375        IUserManager userManager,
 376        ILibraryManager libraryManager,
 377        IDtoService dtoService,
 378        IMediaSourceManager mediaSourceManager,
 379        ITranscodeManager transcodeManager,
 380        ISchedulesDirectService schedulesDirectService)
 81    {
 382        _liveTvManager = liveTvManager;
 383        _guideManager = guideManager;
 384        _tunerHostManager = tunerHostManager;
 385        _listingsManager = listingsManager;
 386        _recordingsManager = recordingsManager;
 387        _userManager = userManager;
 388        _libraryManager = libraryManager;
 389        _dtoService = dtoService;
 390        _mediaSourceManager = mediaSourceManager;
 391        _transcodeManager = transcodeManager;
 392        _schedulesDirectService = schedulesDirectService;
 393    }
 94
 95    /// <summary>
 96    /// Gets available live tv services.
 97    /// </summary>
 98    /// <response code="200">Available live tv services returned.</response>
 99    /// <returns>
 100    /// An <see cref="OkResult"/> containing the available live tv services.
 101    /// </returns>
 102    [HttpGet("Info")]
 103    [ProducesResponseType(StatusCodes.Status200OK)]
 104    [Authorize(Policy = Policies.LiveTvAccess)]
 105    public ActionResult<LiveTvInfo> GetLiveTvInfo()
 106    {
 0107        return _liveTvManager.GetLiveTvInfo(CancellationToken.None);
 108    }
 109
 110    /// <summary>
 111    /// Gets available live tv channels.
 112    /// </summary>
 113    /// <param name="type">Optional. Filter by channel type.</param>
 114    /// <param name="userId">Optional. Filter by user and attach user data.</param>
 115    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 116    /// <param name="isMovie">Optional. Filter for movies.</param>
 117    /// <param name="isSeries">Optional. Filter for series.</param>
 118    /// <param name="isNews">Optional. Filter for news.</param>
 119    /// <param name="isKids">Optional. Filter for kids.</param>
 120    /// <param name="isSports">Optional. Filter for sports.</param>
 121    /// <param name="limit">Optional. The maximum number of records to return.</param>
 122    /// <param name="isFavorite">Optional. Filter by channels that are favorites, or not.</param>
 123    /// <param name="isLiked">Optional. Filter by channels that are liked, or not.</param>
 124    /// <param name="isDisliked">Optional. Filter by channels that are disliked, or not.</param>
 125    /// <param name="enableImages">Optional. Include image information in output.</param>
 126    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 127    /// <param name="enableImageTypes">"Optional. The image types to include in the output.</param>
 128    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 129    /// <param name="enableUserData">Optional. Include user data.</param>
 130    /// <param name="sortBy">Optional. Key to sort by.</param>
 131    /// <param name="sortOrder">Optional. Sort order.</param>
 132    /// <param name="enableFavoriteSorting">Optional. Incorporate favorite and like status into channel sorting.</param>
 133    /// <param name="addCurrentProgram">Optional. Adds current program info to each channel.</param>
 134    /// <response code="200">Available live tv channels returned.</response>
 135    /// <returns>
 136    /// An <see cref="OkResult"/> containing the resulting available live tv channels.
 137    /// </returns>
 138    [HttpGet("Channels")]
 139    [ProducesResponseType(StatusCodes.Status200OK)]
 140    [Authorize(Policy = Policies.LiveTvAccess)]
 141    public ActionResult<QueryResult<BaseItemDto>> GetLiveTvChannels(
 142        [FromQuery] ChannelType? type,
 143        [FromQuery] Guid? userId,
 144        [FromQuery] int? startIndex,
 145        [FromQuery] bool? isMovie,
 146        [FromQuery] bool? isSeries,
 147        [FromQuery] bool? isNews,
 148        [FromQuery] bool? isKids,
 149        [FromQuery] bool? isSports,
 150        [FromQuery] int? limit,
 151        [FromQuery] bool? isFavorite,
 152        [FromQuery] bool? isLiked,
 153        [FromQuery] bool? isDisliked,
 154        [FromQuery] bool? enableImages,
 155        [FromQuery] int? imageTypeLimit,
 156        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 157        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 158        [FromQuery] bool? enableUserData,
 159        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy,
 160        [FromQuery] SortOrder? sortOrder,
 161        [FromQuery] bool enableFavoriteSorting = false,
 162        [FromQuery] bool addCurrentProgram = true)
 163    {
 0164        userId = RequestHelpers.GetUserId(User, userId);
 0165        var dtoOptions = new DtoOptions { Fields = fields }
 0166            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 167
 0168        var channelResult = _liveTvManager.GetInternalChannels(
 0169            new LiveTvChannelQuery
 0170            {
 0171                ChannelType = type,
 0172                UserId = userId.Value,
 0173                StartIndex = startIndex,
 0174                Limit = limit,
 0175                IsFavorite = isFavorite,
 0176                IsLiked = isLiked,
 0177                IsDisliked = isDisliked,
 0178                EnableFavoriteSorting = enableFavoriteSorting,
 0179                IsMovie = isMovie,
 0180                IsSeries = isSeries,
 0181                IsNews = isNews,
 0182                IsKids = isKids,
 0183                IsSports = isSports,
 0184                SortBy = sortBy,
 0185                SortOrder = sortOrder ?? SortOrder.Ascending,
 0186                AddCurrentProgram = addCurrentProgram
 0187            },
 0188            dtoOptions,
 0189            CancellationToken.None);
 190
 0191        var user = userId.IsNullOrEmpty()
 0192            ? null
 0193            : _userManager.GetUserById(userId.Value);
 194
 0195        var fieldsList = dtoOptions.Fields.ToList();
 0196        fieldsList.Remove(ItemFields.CanDelete);
 0197        fieldsList.Remove(ItemFields.CanDownload);
 0198        fieldsList.Remove(ItemFields.DisplayPreferencesId);
 0199        fieldsList.Remove(ItemFields.Etag);
 0200        dtoOptions.Fields = fieldsList.ToArray();
 0201        dtoOptions.AddCurrentProgram = addCurrentProgram;
 202
 0203        var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, dtoOptions, user);
 0204        return new QueryResult<BaseItemDto>(
 0205            startIndex,
 0206            channelResult.TotalRecordCount,
 0207            returnArray);
 208    }
 209
 210    /// <summary>
 211    /// Gets a live tv channel.
 212    /// </summary>
 213    /// <param name="channelId">Channel id.</param>
 214    /// <param name="userId">Optional. Attach user data.</param>
 215    /// <response code="200">Live tv channel returned.</response>
 216    /// <response code="404">Item not found.</response>
 217    /// <returns>An <see cref="OkResult"/> containing the live tv channel.</returns>
 218    [HttpGet("Channels/{channelId}")]
 219    [ProducesResponseType(StatusCodes.Status200OK)]
 220    [ProducesResponseType(StatusCodes.Status404NotFound)]
 221    [Authorize(Policy = Policies.LiveTvAccess)]
 222    public ActionResult<BaseItemDto> GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId)
 223    {
 0224        userId = RequestHelpers.GetUserId(User, userId);
 0225        var user = userId.IsNullOrEmpty()
 0226            ? null
 0227            : _userManager.GetUserById(userId.Value);
 0228        var item = channelId.IsEmpty()
 0229            ? _libraryManager.GetUserRootFolder()
 0230            : _libraryManager.GetItemById<BaseItem>(channelId, user);
 231
 0232        if (item is null)
 233        {
 0234            return NotFound();
 235        }
 236
 0237        var dtoOptions = new DtoOptions();
 0238        return _dtoService.GetBaseItemDto(item, dtoOptions, user);
 239    }
 240
 241    /// <summary>
 242    /// Gets live tv recordings.
 243    /// </summary>
 244    /// <param name="channelId">Optional. Filter by channel id.</param>
 245    /// <param name="userId">Optional. Filter by user and attach user data.</param>
 246    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 247    /// <param name="limit">Optional. The maximum number of records to return.</param>
 248    /// <param name="status">Optional. Filter by recording status.</param>
 249    /// <param name="isInProgress">Optional. Filter by recordings that are in progress, or not.</param>
 250    /// <param name="seriesTimerId">Optional. Filter by recordings belonging to a series timer.</param>
 251    /// <param name="enableImages">Optional. Include image information in output.</param>
 252    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 253    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 254    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 255    /// <param name="enableUserData">Optional. Include user data.</param>
 256    /// <param name="isMovie">Optional. Filter for movies.</param>
 257    /// <param name="isSeries">Optional. Filter for series.</param>
 258    /// <param name="isKids">Optional. Filter for kids.</param>
 259    /// <param name="isSports">Optional. Filter for sports.</param>
 260    /// <param name="isNews">Optional. Filter for news.</param>
 261    /// <param name="isLibraryItem">Optional. Filter for is library item.</param>
 262    /// <param name="enableTotalRecordCount">Optional. Return total record count.</param>
 263    /// <response code="200">Live tv recordings returned.</response>
 264    /// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
 265    [HttpGet("Recordings")]
 266    [ProducesResponseType(StatusCodes.Status200OK)]
 267    [Authorize(Policy = Policies.LiveTvAccess)]
 268    public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecordings(
 269        [FromQuery] string? channelId,
 270        [FromQuery] Guid? userId,
 271        [FromQuery] int? startIndex,
 272        [FromQuery] int? limit,
 273        [FromQuery] RecordingStatus? status,
 274        [FromQuery] bool? isInProgress,
 275        [FromQuery] string? seriesTimerId,
 276        [FromQuery] bool? enableImages,
 277        [FromQuery] int? imageTypeLimit,
 278        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 279        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 280        [FromQuery] bool? enableUserData,
 281        [FromQuery] bool? isMovie,
 282        [FromQuery] bool? isSeries,
 283        [FromQuery] bool? isKids,
 284        [FromQuery] bool? isSports,
 285        [FromQuery] bool? isNews,
 286        [FromQuery] bool? isLibraryItem,
 287        [FromQuery] bool enableTotalRecordCount = true)
 288    {
 0289        userId = RequestHelpers.GetUserId(User, userId);
 0290        var dtoOptions = new DtoOptions { Fields = fields }
 0291            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 292
 0293        return await _liveTvManager.GetRecordingsAsync(
 0294            new RecordingQuery
 0295            {
 0296                ChannelId = channelId,
 0297                UserId = userId.Value,
 0298                StartIndex = startIndex,
 0299                Limit = limit,
 0300                Status = status,
 0301                SeriesTimerId = seriesTimerId,
 0302                IsInProgress = isInProgress,
 0303                EnableTotalRecordCount = enableTotalRecordCount,
 0304                IsMovie = isMovie,
 0305                IsNews = isNews,
 0306                IsSeries = isSeries,
 0307                IsKids = isKids,
 0308                IsSports = isSports,
 0309                IsLibraryItem = isLibraryItem,
 0310                Fields = fields,
 0311                ImageTypeLimit = imageTypeLimit,
 0312                EnableImages = enableImages
 0313            },
 0314            dtoOptions).ConfigureAwait(false);
 0315    }
 316
 317    /// <summary>
 318    /// Gets live tv recording series.
 319    /// </summary>
 320    /// <param name="channelId">Optional. Filter by channel id.</param>
 321    /// <param name="userId">Optional. Filter by user and attach user data.</param>
 322    /// <param name="groupId">Optional. Filter by recording group.</param>
 323    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 324    /// <param name="limit">Optional. The maximum number of records to return.</param>
 325    /// <param name="status">Optional. Filter by recording status.</param>
 326    /// <param name="isInProgress">Optional. Filter by recordings that are in progress, or not.</param>
 327    /// <param name="seriesTimerId">Optional. Filter by recordings belonging to a series timer.</param>
 328    /// <param name="enableImages">Optional. Include image information in output.</param>
 329    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 330    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 331    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 332    /// <param name="enableUserData">Optional. Include user data.</param>
 333    /// <param name="enableTotalRecordCount">Optional. Return total record count.</param>
 334    /// <response code="200">Live tv recordings returned.</response>
 335    /// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
 336    [HttpGet("Recordings/Series")]
 337    [ProducesResponseType(StatusCodes.Status200OK)]
 338    [Authorize(Policy = Policies.LiveTvAccess)]
 339    [Obsolete("This endpoint is obsolete.")]
 340    [ApiExplorerSettings(IgnoreApi = true)]
 341    public ActionResult<QueryResult<BaseItemDto>> GetRecordingsSeries(
 342        [FromQuery] string? channelId,
 343        [FromQuery] Guid? userId,
 344        [FromQuery] string? groupId,
 345        [FromQuery] int? startIndex,
 346        [FromQuery] int? limit,
 347        [FromQuery] RecordingStatus? status,
 348        [FromQuery] bool? isInProgress,
 349        [FromQuery] string? seriesTimerId,
 350        [FromQuery] bool? enableImages,
 351        [FromQuery] int? imageTypeLimit,
 352        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 353        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 354        [FromQuery] bool? enableUserData,
 355        [FromQuery] bool enableTotalRecordCount = true)
 356    {
 357        return new QueryResult<BaseItemDto>();
 358    }
 359
 360    /// <summary>
 361    /// Gets live tv recording groups.
 362    /// </summary>
 363    /// <param name="userId">Optional. Filter by user and attach user data.</param>
 364    /// <response code="200">Recording groups returned.</response>
 365    /// <returns>An <see cref="OkResult"/> containing the recording groups.</returns>
 366    [HttpGet("Recordings/Groups")]
 367    [ProducesResponseType(StatusCodes.Status200OK)]
 368    [Authorize(Policy = Policies.LiveTvAccess)]
 369    [Obsolete("This endpoint is obsolete.")]
 370    [ApiExplorerSettings(IgnoreApi = true)]
 371    public ActionResult<QueryResult<BaseItemDto>> GetRecordingGroups([FromQuery] Guid? userId)
 372    {
 373        return new QueryResult<BaseItemDto>();
 374    }
 375
 376    /// <summary>
 377    /// Gets recording folders.
 378    /// </summary>
 379    /// <param name="userId">Optional. Filter by user and attach user data.</param>
 380    /// <response code="200">Recording folders returned.</response>
 381    /// <returns>An <see cref="OkResult"/> containing the recording folders.</returns>
 382    [HttpGet("Recordings/Folders")]
 383    [ProducesResponseType(StatusCodes.Status200OK)]
 384    [Authorize(Policy = Policies.LiveTvAccess)]
 385    public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecordingFolders([FromQuery] Guid? userId)
 386    {
 0387        userId = RequestHelpers.GetUserId(User, userId);
 0388        var user = userId.IsNullOrEmpty()
 0389            ? null
 0390            : _userManager.GetUserById(userId.Value);
 0391        var folders = await _liveTvManager.GetRecordingFoldersAsync(user).ConfigureAwait(false);
 392
 0393        var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user);
 394
 0395        return new QueryResult<BaseItemDto>(returnArray);
 0396    }
 397
 398    /// <summary>
 399    /// Gets a live tv recording.
 400    /// </summary>
 401    /// <param name="recordingId">Recording id.</param>
 402    /// <param name="userId">Optional. Attach user data.</param>
 403    /// <response code="200">Recording returned.</response>
 404    /// <response code="404">Item not found.</response>
 405    /// <returns>An <see cref="OkResult"/> containing the live tv recording.</returns>
 406    [HttpGet("Recordings/{recordingId}")]
 407    [ProducesResponseType(StatusCodes.Status200OK)]
 408    [ProducesResponseType(StatusCodes.Status404NotFound)]
 409    [Authorize(Policy = Policies.LiveTvAccess)]
 410    public ActionResult<BaseItemDto> GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId)
 411    {
 0412        userId = RequestHelpers.GetUserId(User, userId);
 0413        var user = userId.IsNullOrEmpty()
 0414            ? null
 0415            : _userManager.GetUserById(userId.Value);
 0416        var item = recordingId.IsEmpty()
 0417            ? _libraryManager.GetUserRootFolder()
 0418            : _libraryManager.GetItemById<BaseItem>(recordingId, user);
 0419        if (item is null)
 420        {
 0421            return NotFound();
 422        }
 423
 0424        var dtoOptions = new DtoOptions();
 425
 0426        return _dtoService.GetBaseItemDto(item, dtoOptions, user);
 427    }
 428
 429    /// <summary>
 430    /// Resets a tv tuner.
 431    /// </summary>
 432    /// <param name="tunerId">Tuner id.</param>
 433    /// <response code="204">Tuner reset.</response>
 434    /// <returns>A <see cref="NoContentResult"/>.</returns>
 435    [HttpPost("Tuners/{tunerId}/Reset")]
 436    [ProducesResponseType(StatusCodes.Status204NoContent)]
 437    [Authorize(Policy = Policies.RequiresElevation)]
 438    public async Task<ActionResult> ResetTuner([FromRoute, Required] string tunerId)
 439    {
 0440        await _liveTvManager.ResetTuner(tunerId, CancellationToken.None).ConfigureAwait(false);
 0441        return NoContent();
 0442    }
 443
 444    /// <summary>
 445    /// Gets a timer.
 446    /// </summary>
 447    /// <param name="timerId">Timer id.</param>
 448    /// <response code="200">Timer returned.</response>
 449    /// <returns>
 450    /// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the timer.
 451    /// </returns>
 452    [HttpGet("Timers/{timerId}")]
 453    [ProducesResponseType(StatusCodes.Status200OK)]
 454    [Authorize(Policy = Policies.LiveTvAccess)]
 455    public async Task<ActionResult<TimerInfoDto>> GetTimer([FromRoute, Required] string timerId)
 456    {
 0457        return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false);
 0458    }
 459
 460    /// <summary>
 461    /// Gets the default values for a new timer.
 462    /// </summary>
 463    /// <param name="programId">Optional. To attach default values based on a program.</param>
 464    /// <response code="200">Default values returned.</response>
 465    /// <returns>
 466    /// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the default values for a timer.
 467    /// </returns>
 468    [HttpGet("Timers/Defaults")]
 469    [ProducesResponseType(StatusCodes.Status200OK)]
 470    [Authorize(Policy = Policies.LiveTvAccess)]
 471    public async Task<ActionResult<SeriesTimerInfoDto>> GetDefaultTimer([FromQuery] string? programId)
 472    {
 0473        return string.IsNullOrEmpty(programId)
 0474            ? await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false)
 0475            : await _liveTvManager.GetNewTimerDefaults(programId, CancellationToken.None).ConfigureAwait(false);
 0476    }
 477
 478    /// <summary>
 479    /// Gets the live tv timers.
 480    /// </summary>
 481    /// <param name="channelId">Optional. Filter by channel id.</param>
 482    /// <param name="seriesTimerId">Optional. Filter by timers belonging to a series timer.</param>
 483    /// <param name="isActive">Optional. Filter by timers that are active.</param>
 484    /// <param name="isScheduled">Optional. Filter by timers that are scheduled.</param>
 485    /// <returns>
 486    /// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the live tv timers.
 487    /// </returns>
 488    [HttpGet("Timers")]
 489    [ProducesResponseType(StatusCodes.Status200OK)]
 490    [Authorize(Policy = Policies.LiveTvAccess)]
 491    public async Task<ActionResult<QueryResult<TimerInfoDto>>> GetTimers(
 492        [FromQuery] string? channelId,
 493        [FromQuery] string? seriesTimerId,
 494        [FromQuery] bool? isActive,
 495        [FromQuery] bool? isScheduled)
 496    {
 0497        return await _liveTvManager.GetTimers(
 0498            new TimerQuery
 0499            {
 0500                ChannelId = channelId,
 0501                SeriesTimerId = seriesTimerId,
 0502                IsActive = isActive,
 0503                IsScheduled = isScheduled
 0504            },
 0505            CancellationToken.None).ConfigureAwait(false);
 0506    }
 507
 508    /// <summary>
 509    /// Gets available live tv epgs.
 510    /// </summary>
 511    /// <param name="channelIds">The channels to return guide information for.</param>
 512    /// <param name="userId">Optional. Filter by user id.</param>
 513    /// <param name="minStartDate">Optional. The minimum premiere start date.</param>
 514    /// <param name="hasAired">Optional. Filter by programs that have completed airing, or not.</param>
 515    /// <param name="isAiring">Optional. Filter by programs that are currently airing, or not.</param>
 516    /// <param name="maxStartDate">Optional. The maximum premiere start date.</param>
 517    /// <param name="minEndDate">Optional. The minimum premiere end date.</param>
 518    /// <param name="maxEndDate">Optional. The maximum premiere end date.</param>
 519    /// <param name="isMovie">Optional. Filter for movies.</param>
 520    /// <param name="isSeries">Optional. Filter for series.</param>
 521    /// <param name="isNews">Optional. Filter for news.</param>
 522    /// <param name="isKids">Optional. Filter for kids.</param>
 523    /// <param name="isSports">Optional. Filter for sports.</param>
 524    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 525    /// <param name="limit">Optional. The maximum number of records to return.</param>
 526    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Name, StartDate.</para
 527    /// <param name="sortOrder">Sort Order - Ascending,Descending.</param>
 528    /// <param name="genres">The genres to return guide information for.</param>
 529    /// <param name="genreIds">The genre ids to return guide information for.</param>
 530    /// <param name="enableImages">Optional. Include image information in output.</param>
 531    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 532    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 533    /// <param name="enableUserData">Optional. Include user data.</param>
 534    /// <param name="seriesTimerId">Optional. Filter by series timer id.</param>
 535    /// <param name="librarySeriesId">Optional. Filter by library series id.</param>
 536    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 537    /// <param name="enableTotalRecordCount">Retrieve total record count.</param>
 538    /// <response code="200">Live tv epgs returned.</response>
 539    /// <returns>
 540    /// A <see cref="Task"/> containing a <see cref="OkResult"/> which contains the live tv epgs.
 541    /// </returns>
 542    [HttpGet("Programs")]
 543    [ProducesResponseType(StatusCodes.Status200OK)]
 544    [Authorize(Policy = Policies.LiveTvAccess)]
 545    public async Task<ActionResult<QueryResult<BaseItemDto>>> GetLiveTvPrograms(
 546        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] channelIds,
 547        [FromQuery] Guid? userId,
 548        [FromQuery] DateTime? minStartDate,
 549        [FromQuery] bool? hasAired,
 550        [FromQuery] bool? isAiring,
 551        [FromQuery] DateTime? maxStartDate,
 552        [FromQuery] DateTime? minEndDate,
 553        [FromQuery] DateTime? maxEndDate,
 554        [FromQuery] bool? isMovie,
 555        [FromQuery] bool? isSeries,
 556        [FromQuery] bool? isNews,
 557        [FromQuery] bool? isKids,
 558        [FromQuery] bool? isSports,
 559        [FromQuery] int? startIndex,
 560        [FromQuery] int? limit,
 561        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy,
 562        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[] sortOrder,
 563        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] genres,
 564        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] genreIds,
 565        [FromQuery] bool? enableImages,
 566        [FromQuery] int? imageTypeLimit,
 567        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 568        [FromQuery] bool? enableUserData,
 569        [FromQuery] string? seriesTimerId,
 570        [FromQuery] Guid? librarySeriesId,
 571        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 572        [FromQuery] bool enableTotalRecordCount = true)
 573    {
 0574        userId = RequestHelpers.GetUserId(User, userId);
 0575        var user = userId.IsNullOrEmpty()
 0576            ? null
 0577            : _userManager.GetUserById(userId.Value);
 578
 0579        var query = new InternalItemsQuery(user)
 0580        {
 0581            ChannelIds = channelIds,
 0582            HasAired = hasAired,
 0583            IsAiring = isAiring,
 0584            EnableTotalRecordCount = enableTotalRecordCount,
 0585            MinStartDate = minStartDate,
 0586            MinEndDate = minEndDate,
 0587            MaxStartDate = maxStartDate,
 0588            MaxEndDate = maxEndDate,
 0589            StartIndex = startIndex,
 0590            Limit = limit,
 0591            OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
 0592            IsNews = isNews,
 0593            IsMovie = isMovie,
 0594            IsSeries = isSeries,
 0595            IsKids = isKids,
 0596            IsSports = isSports,
 0597            SeriesTimerId = seriesTimerId,
 0598            Genres = genres,
 0599            GenreIds = genreIds
 0600        };
 601
 0602        if (!librarySeriesId.IsNullOrEmpty())
 603        {
 0604            query.IsSeries = true;
 605
 0606            var series = _libraryManager.GetItemById<Series>(librarySeriesId.Value);
 0607            if (series is not null)
 608            {
 0609                query.Name = series.Name;
 610            }
 611        }
 612
 0613        var dtoOptions = new DtoOptions { Fields = fields }
 0614            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 0615        return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
 0616    }
 617
 618    /// <summary>
 619    /// Gets available live tv epgs.
 620    /// </summary>
 621    /// <param name="body">Request body.</param>
 622    /// <response code="200">Live tv epgs returned.</response>
 623    /// <returns>
 624    /// A <see cref="Task"/> containing a <see cref="OkResult"/> which contains the live tv epgs.
 625    /// </returns>
 626    [HttpPost("Programs")]
 627    [ProducesResponseType(StatusCodes.Status200OK)]
 628    [Authorize(Policy = Policies.LiveTvAccess)]
 629    public async Task<ActionResult<QueryResult<BaseItemDto>>> GetPrograms([FromBody] GetProgramsDto body)
 630    {
 0631        var user = body.UserId.IsNullOrEmpty() ? null : _userManager.GetUserById(body.UserId.Value);
 632
 0633        var query = new InternalItemsQuery(user)
 0634        {
 0635            ChannelIds = body.ChannelIds ?? [],
 0636            HasAired = body.HasAired,
 0637            IsAiring = body.IsAiring,
 0638            EnableTotalRecordCount = body.EnableTotalRecordCount,
 0639            MinStartDate = body.MinStartDate,
 0640            MinEndDate = body.MinEndDate,
 0641            MaxStartDate = body.MaxStartDate,
 0642            MaxEndDate = body.MaxEndDate,
 0643            StartIndex = body.StartIndex,
 0644            Limit = body.Limit,
 0645            OrderBy = RequestHelpers.GetOrderBy(body.SortBy ?? [], body.SortOrder ?? []),
 0646            IsNews = body.IsNews,
 0647            IsMovie = body.IsMovie,
 0648            IsSeries = body.IsSeries,
 0649            IsKids = body.IsKids,
 0650            IsSports = body.IsSports,
 0651            SeriesTimerId = body.SeriesTimerId,
 0652            Genres = body.Genres ?? [],
 0653            GenreIds = body.GenreIds ?? []
 0654        };
 655
 0656        if (!body.LibrarySeriesId.IsNullOrEmpty())
 657        {
 0658            query.IsSeries = true;
 659
 0660            var series = _libraryManager.GetItemById<Series>(body.LibrarySeriesId.Value);
 0661            if (series is not null)
 662            {
 0663                query.Name = series.Name;
 664            }
 665        }
 666
 0667        var dtoOptions = new DtoOptions { Fields = body.Fields ?? [] }
 0668            .AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes 
 0669        return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
 0670    }
 671
 672    /// <summary>
 673    /// Gets recommended live tv epgs.
 674    /// </summary>
 675    /// <param name="userId">Optional. filter by user id.</param>
 676    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 677    /// <param name="limit">Optional. The maximum number of records to return.</param>
 678    /// <param name="isAiring">Optional. Filter by programs that are currently airing, or not.</param>
 679    /// <param name="hasAired">Optional. Filter by programs that have completed airing, or not.</param>
 680    /// <param name="isSeries">Optional. Filter for series.</param>
 681    /// <param name="isMovie">Optional. Filter for movies.</param>
 682    /// <param name="isNews">Optional. Filter for news.</param>
 683    /// <param name="isKids">Optional. Filter for kids.</param>
 684    /// <param name="isSports">Optional. Filter for sports.</param>
 685    /// <param name="enableImages">Optional. Include image information in output.</param>
 686    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 687    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 688    /// <param name="genreIds">The genres to return guide information for.</param>
 689    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 690    /// <param name="enableUserData">Optional. include user data.</param>
 691    /// <param name="enableTotalRecordCount">Retrieve total record count.</param>
 692    /// <response code="200">Recommended epgs returned.</response>
 693    /// <returns>A <see cref="OkResult"/> containing the queryresult of recommended epgs.</returns>
 694    [HttpGet("Programs/Recommended")]
 695    [Authorize(Policy = Policies.LiveTvAccess)]
 696    [ProducesResponseType(StatusCodes.Status200OK)]
 697    public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecommendedPrograms(
 698        [FromQuery] Guid? userId,
 699        [FromQuery] int? startIndex,
 700        [FromQuery] int? limit,
 701        [FromQuery] bool? isAiring,
 702        [FromQuery] bool? hasAired,
 703        [FromQuery] bool? isSeries,
 704        [FromQuery] bool? isMovie,
 705        [FromQuery] bool? isNews,
 706        [FromQuery] bool? isKids,
 707        [FromQuery] bool? isSports,
 708        [FromQuery] bool? enableImages,
 709        [FromQuery] int? imageTypeLimit,
 710        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 711        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] genreIds,
 712        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 713        [FromQuery] bool? enableUserData,
 714        [FromQuery] bool enableTotalRecordCount = true)
 715    {
 0716        userId = RequestHelpers.GetUserId(User, userId);
 0717        var user = userId.IsNullOrEmpty()
 0718            ? null
 0719            : _userManager.GetUserById(userId.Value);
 720
 0721        var query = new InternalItemsQuery(user)
 0722        {
 0723            IsAiring = isAiring,
 0724            StartIndex = startIndex,
 0725            Limit = limit,
 0726            HasAired = hasAired,
 0727            IsSeries = isSeries,
 0728            IsMovie = isMovie,
 0729            IsKids = isKids,
 0730            IsNews = isNews,
 0731            IsSports = isSports,
 0732            EnableTotalRecordCount = enableTotalRecordCount,
 0733            GenreIds = genreIds
 0734        };
 735
 0736        var dtoOptions = new DtoOptions { Fields = fields }
 0737            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 0738        return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwai
 0739    }
 740
 741    /// <summary>
 742    /// Gets a live tv program.
 743    /// </summary>
 744    /// <param name="programId">Program id.</param>
 745    /// <param name="userId">Optional. Attach user data.</param>
 746    /// <response code="200">Program returned.</response>
 747    /// <returns>An <see cref="OkResult"/> containing the livetv program.</returns>
 748    [HttpGet("Programs/{programId}")]
 749    [Authorize(Policy = Policies.LiveTvAccess)]
 750    [ProducesResponseType(StatusCodes.Status200OK)]
 751    public async Task<ActionResult<BaseItemDto>> GetProgram(
 752        [FromRoute, Required] string programId,
 753        [FromQuery] Guid? userId)
 754    {
 0755        userId = RequestHelpers.GetUserId(User, userId);
 0756        var user = userId.IsNullOrEmpty()
 0757            ? null
 0758            : _userManager.GetUserById(userId.Value);
 759
 0760        return await _liveTvManager.GetProgram(programId, CancellationToken.None, user).ConfigureAwait(false);
 0761    }
 762
 763    /// <summary>
 764    /// Deletes a live tv recording.
 765    /// </summary>
 766    /// <param name="recordingId">Recording id.</param>
 767    /// <response code="204">Recording deleted.</response>
 768    /// <response code="404">Item not found.</response>
 769    /// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</retur
 770    [HttpDelete("Recordings/{recordingId}")]
 771    [Authorize(Policy = Policies.LiveTvManagement)]
 772    [ProducesResponseType(StatusCodes.Status204NoContent)]
 773    [ProducesResponseType(StatusCodes.Status404NotFound)]
 774    public ActionResult DeleteRecording([FromRoute, Required] Guid recordingId)
 775    {
 0776        var item = _libraryManager.GetItemById<BaseItem>(recordingId, User.GetUserId());
 0777        if (item is null)
 778        {
 0779            return NotFound();
 780        }
 781
 0782        _libraryManager.DeleteItem(item, new DeleteOptions
 0783        {
 0784            DeleteFileLocation = false
 0785        });
 786
 0787        return NoContent();
 788    }
 789
 790    /// <summary>
 791    /// Cancels a live tv timer.
 792    /// </summary>
 793    /// <param name="timerId">Timer id.</param>
 794    /// <response code="204">Timer deleted.</response>
 795    /// <returns>A <see cref="NoContentResult"/>.</returns>
 796    [HttpDelete("Timers/{timerId}")]
 797    [Authorize(Policy = Policies.LiveTvManagement)]
 798    [ProducesResponseType(StatusCodes.Status204NoContent)]
 799    public async Task<ActionResult> CancelTimer([FromRoute, Required] string timerId)
 800    {
 0801        await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false);
 0802        return NoContent();
 0803    }
 804
 805    /// <summary>
 806    /// Updates a live tv timer.
 807    /// </summary>
 808    /// <param name="timerId">Timer id.</param>
 809    /// <param name="timerInfo">New timer info.</param>
 810    /// <response code="204">Timer updated.</response>
 811    /// <returns>A <see cref="NoContentResult"/>.</returns>
 812    [HttpPost("Timers/{timerId}")]
 813    [Authorize(Policy = Policies.LiveTvManagement)]
 814    [ProducesResponseType(StatusCodes.Status204NoContent)]
 815    public async Task<ActionResult> UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo)
 816    {
 0817        await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false);
 0818        return NoContent();
 0819    }
 820
 821    /// <summary>
 822    /// Creates a live tv timer.
 823    /// </summary>
 824    /// <param name="timerInfo">New timer info.</param>
 825    /// <response code="204">Timer created.</response>
 826    /// <returns>A <see cref="NoContentResult"/>.</returns>
 827    [HttpPost("Timers")]
 828    [Authorize(Policy = Policies.LiveTvManagement)]
 829    [ProducesResponseType(StatusCodes.Status204NoContent)]
 830    public async Task<ActionResult> CreateTimer([FromBody] TimerInfoDto timerInfo)
 831    {
 0832        await _liveTvManager.CreateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false);
 0833        return NoContent();
 0834    }
 835
 836    /// <summary>
 837    /// Gets a live tv series timer.
 838    /// </summary>
 839    /// <param name="timerId">Timer id.</param>
 840    /// <response code="200">Series timer returned.</response>
 841    /// <response code="404">Series timer not found.</response>
 842    /// <returns>A <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if timer not found.</returns>
 843    [HttpGet("SeriesTimers/{timerId}")]
 844    [Authorize(Policy = Policies.LiveTvAccess)]
 845    [ProducesResponseType(StatusCodes.Status200OK)]
 846    [ProducesResponseType(StatusCodes.Status404NotFound)]
 847    public async Task<ActionResult<SeriesTimerInfoDto>> GetSeriesTimer([FromRoute, Required] string timerId)
 848    {
 0849        var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false);
 0850        if (timer is null)
 851        {
 0852            return NotFound();
 853        }
 854
 0855        return timer;
 0856    }
 857
 858    /// <summary>
 859    /// Gets live tv series timers.
 860    /// </summary>
 861    /// <param name="sortBy">Optional. Sort by SortName or Priority.</param>
 862    /// <param name="sortOrder">Optional. Sort in Ascending or Descending order.</param>
 863    /// <response code="200">Timers returned.</response>
 864    /// <returns>An <see cref="OkResult"/> of live tv series timers.</returns>
 865    [HttpGet("SeriesTimers")]
 866    [Authorize(Policy = Policies.LiveTvAccess)]
 867    [ProducesResponseType(StatusCodes.Status200OK)]
 868    public async Task<ActionResult<QueryResult<SeriesTimerInfoDto>>> GetSeriesTimers([FromQuery] string? sortBy, [FromQu
 869    {
 0870        return await _liveTvManager.GetSeriesTimers(
 0871            new SeriesTimerQuery
 0872            {
 0873                SortOrder = sortOrder ?? SortOrder.Ascending,
 0874                SortBy = sortBy
 0875            },
 0876            CancellationToken.None).ConfigureAwait(false);
 0877    }
 878
 879    /// <summary>
 880    /// Cancels a live tv series timer.
 881    /// </summary>
 882    /// <param name="timerId">Timer id.</param>
 883    /// <response code="204">Timer cancelled.</response>
 884    /// <returns>A <see cref="NoContentResult"/>.</returns>
 885    [HttpDelete("SeriesTimers/{timerId}")]
 886    [Authorize(Policy = Policies.LiveTvManagement)]
 887    [ProducesResponseType(StatusCodes.Status204NoContent)]
 888    public async Task<ActionResult> CancelSeriesTimer([FromRoute, Required] string timerId)
 889    {
 0890        await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false);
 0891        return NoContent();
 0892    }
 893
 894    /// <summary>
 895    /// Updates a live tv series timer.
 896    /// </summary>
 897    /// <param name="timerId">Timer id.</param>
 898    /// <param name="seriesTimerInfo">New series timer info.</param>
 899    /// <response code="204">Series timer updated.</response>
 900    /// <returns>A <see cref="NoContentResult"/>.</returns>
 901    [HttpPost("SeriesTimers/{timerId}")]
 902    [Authorize(Policy = Policies.LiveTvManagement)]
 903    [ProducesResponseType(StatusCodes.Status204NoContent)]
 904    public async Task<ActionResult> UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDt
 905    {
 0906        await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false);
 0907        return NoContent();
 0908    }
 909
 910    /// <summary>
 911    /// Creates a live tv series timer.
 912    /// </summary>
 913    /// <param name="seriesTimerInfo">New series timer info.</param>
 914    /// <response code="204">Series timer info created.</response>
 915    /// <returns>A <see cref="NoContentResult"/>.</returns>
 916    [HttpPost("SeriesTimers")]
 917    [Authorize(Policy = Policies.LiveTvManagement)]
 918    [ProducesResponseType(StatusCodes.Status204NoContent)]
 919    public async Task<ActionResult> CreateSeriesTimer([FromBody] SeriesTimerInfoDto seriesTimerInfo)
 920    {
 0921        await _liveTvManager.CreateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false);
 0922        return NoContent();
 0923    }
 924
 925    /// <summary>
 926    /// Get guide info.
 927    /// </summary>
 928    /// <response code="200">Guide info returned.</response>
 929    /// <returns>An <see cref="OkResult"/> containing the guide info.</returns>
 930    [HttpGet("GuideInfo")]
 931    [Authorize(Policy = Policies.LiveTvAccess)]
 932    [ProducesResponseType(StatusCodes.Status200OK)]
 933    public ActionResult<GuideInfo> GetGuideInfo()
 0934        => _guideManager.GetGuideInfo();
 935
 936    /// <summary>
 937    /// Adds a tuner host.
 938    /// </summary>
 939    /// <param name="tunerHostInfo">New tuner host.</param>
 940    /// <response code="200">Created tuner host returned.</response>
 941    /// <returns>A <see cref="OkResult"/> containing the created tuner host.</returns>
 942    [HttpPost("TunerHosts")]
 943    [Authorize(Policy = Policies.RequiresElevation)]
 944    [ProducesResponseType(StatusCodes.Status200OK)]
 945    public async Task<ActionResult<TunerHostInfo>> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo)
 3946        => await _tunerHostManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
 947
 948    /// <summary>
 949    /// Deletes a tuner host.
 950    /// </summary>
 951    /// <param name="id">Tuner host id.</param>
 952    /// <response code="204">Tuner host deleted.</response>
 953    /// <returns>A <see cref="NoContentResult"/>.</returns>
 954    [HttpDelete("TunerHosts")]
 955    [Authorize(Policy = Policies.RequiresElevation)]
 956    [ProducesResponseType(StatusCodes.Status204NoContent)]
 957    public ActionResult DeleteTunerHost([FromQuery] string? id)
 958    {
 0959        _tunerHostManager.DeleteTunerHost(id);
 0960        return NoContent();
 961    }
 962
 963    /// <summary>
 964    /// Gets default listings provider info.
 965    /// </summary>
 966    /// <response code="200">Default listings provider info returned.</response>
 967    /// <returns>An <see cref="OkResult"/> containing the default listings provider info.</returns>
 968    [HttpGet("ListingProviders/Default")]
 969    [Authorize(Policy = Policies.LiveTvAccess)]
 970    [ProducesResponseType(StatusCodes.Status200OK)]
 971    public ActionResult<ListingsProviderInfo> GetDefaultListingProvider()
 972    {
 0973        return new ListingsProviderInfo();
 974    }
 975
 976    /// <summary>
 977    /// Adds a listings provider.
 978    /// </summary>
 979    /// <param name="pw">Password.</param>
 980    /// <param name="listingsProviderInfo">New listings info.</param>
 981    /// <param name="validateListings">Validate listings.</param>
 982    /// <param name="validateLogin">Validate login.</param>
 983    /// <response code="200">Created listings provider returned.</response>
 984    /// <returns>A <see cref="OkResult"/> containing the created listings provider.</returns>
 985    [HttpPost("ListingProviders")]
 986    [Authorize(Policy = Policies.RequiresElevation)]
 987    [ProducesResponseType(StatusCodes.Status200OK)]
 988    [SuppressMessage("Microsoft.Performance", "CA5350:RemoveSha1", MessageId = "AddListingProvider", Justification = "Im
 989    public async Task<ActionResult<ListingsProviderInfo>> AddListingProvider(
 990        [FromQuery] string? pw,
 991        [FromBody] ListingsProviderInfo listingsProviderInfo,
 992        [FromQuery] bool validateListings = false,
 993        [FromQuery] bool validateLogin = false)
 994    {
 0995        if (!string.IsNullOrEmpty(pw))
 996        {
 997            // TODO: remove ToLower when Convert.ToHexString supports lowercase
 998            // Schedules Direct requires the hex to be lowercase
 0999            listingsProviderInfo.Password = Convert.ToHexString(SHA1.HashData(Encoding.UTF8.GetBytes(pw))).ToLowerInvari
 1000        }
 1001
 01002        return await _listingsManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).Configu
 01003    }
 1004
 1005    /// <summary>
 1006    /// Delete listing provider.
 1007    /// </summary>
 1008    /// <param name="id">Listing provider id.</param>
 1009    /// <response code="204">Listing provider deleted.</response>
 1010    /// <returns>A <see cref="NoContentResult"/>.</returns>
 1011    [HttpDelete("ListingProviders")]
 1012    [Authorize(Policy = Policies.RequiresElevation)]
 1013    [ProducesResponseType(StatusCodes.Status204NoContent)]
 1014    public ActionResult DeleteListingProvider([FromQuery] string? id)
 1015    {
 01016        _listingsManager.DeleteListingsProvider(id);
 01017        return NoContent();
 1018    }
 1019
 1020    /// <summary>
 1021    /// Gets available lineups.
 1022    /// </summary>
 1023    /// <param name="id">Provider id.</param>
 1024    /// <param name="type">Provider type.</param>
 1025    /// <param name="location">Location.</param>
 1026    /// <param name="country">Country.</param>
 1027    /// <response code="200">Available lineups returned.</response>
 1028    /// <returns>A <see cref="OkResult"/> containing the available lineups.</returns>
 1029    [HttpGet("ListingProviders/Lineups")]
 1030    [Authorize(Policy = Policies.LiveTvAccess)]
 1031    [ProducesResponseType(StatusCodes.Status200OK)]
 1032    public async Task<ActionResult<IEnumerable<NameIdPair>>> GetLineups(
 1033        [FromQuery] string? id,
 1034        [FromQuery] string? type,
 1035        [FromQuery] string? location,
 1036        [FromQuery] string? country)
 01037        => await _listingsManager.GetLineups(type, id, country, location).ConfigureAwait(false);
 1038
 1039    /// <summary>
 1040    /// Gets available countries.
 1041    /// </summary>
 1042    /// <response code="200">Available countries returned.</response>
 1043    /// <returns>A <see cref="FileResult"/> containing the available countries.</returns>
 1044    [HttpGet("ListingProviders/SchedulesDirect/Countries")]
 1045    [Authorize(Policy = Policies.RequiresElevation)]
 1046    [ProducesResponseType(StatusCodes.Status200OK)]
 1047    [ProducesFile(MediaTypeNames.Application.Json)]
 1048    public async Task<ActionResult> GetSchedulesDirectCountries()
 1049    {
 01050        var stream = await _schedulesDirectService.GetAvailableCountries(CancellationToken.None).ConfigureAwait(false);
 01051        return File(stream, MediaTypeNames.Application.Json);
 01052    }
 1053
 1054    /// <summary>
 1055    /// Get channel mapping options.
 1056    /// </summary>
 1057    /// <param name="providerId">Provider id.</param>
 1058    /// <response code="200">Channel mapping options returned.</response>
 1059    /// <returns>An <see cref="OkResult"/> containing the channel mapping options.</returns>
 1060    [HttpGet("ChannelMappingOptions")]
 1061    [Authorize(Policy = Policies.RequiresElevation)]
 1062    [ProducesResponseType(StatusCodes.Status200OK)]
 1063    public Task<ChannelMappingOptionsDto> GetChannelMappingOptions([FromQuery] string? providerId)
 01064        => _listingsManager.GetChannelMappingOptions(providerId);
 1065
 1066    /// <summary>
 1067    /// Set channel mappings.
 1068    /// </summary>
 1069    /// <param name="dto">The set channel mapping dto.</param>
 1070    /// <response code="200">Created channel mapping returned.</response>
 1071    /// <returns>An <see cref="OkResult"/> containing the created channel mapping.</returns>
 1072    [HttpPost("ChannelMappings")]
 1073    [Authorize(Policy = Policies.RequiresElevation)]
 1074    [ProducesResponseType(StatusCodes.Status200OK)]
 1075    public Task<TunerChannelMapping> SetChannelMapping([FromBody, Required] SetChannelMappingDto dto)
 01076        => _listingsManager.SetChannelMapping(dto.ProviderId, dto.TunerChannelId, dto.ProviderChannelId);
 1077
 1078    /// <summary>
 1079    /// Get tuner host types.
 1080    /// </summary>
 1081    /// <response code="200">Tuner host types returned.</response>
 1082    /// <returns>An <see cref="OkResult"/> containing the tuner host types.</returns>
 1083    [HttpGet("TunerHosts/Types")]
 1084    [Authorize(Policy = Policies.LiveTvAccess)]
 1085    [ProducesResponseType(StatusCodes.Status200OK)]
 1086    public IEnumerable<NameIdPair> GetTunerHostTypes()
 01087        => _tunerHostManager.GetTunerHostTypes();
 1088
 1089    /// <summary>
 1090    /// Discover tuners.
 1091    /// </summary>
 1092    /// <param name="newDevicesOnly">Only discover new tuners.</param>
 1093    /// <response code="200">Tuners returned.</response>
 1094    /// <returns>An <see cref="OkResult"/> containing the tuners.</returns>
 1095    [HttpGet("Tuners/Discvover", Name = "DiscvoverTuners")]
 1096    [HttpGet("Tuners/Discover")]
 1097    [Authorize(Policy = Policies.RequiresElevation)]
 1098    [ProducesResponseType(StatusCodes.Status200OK)]
 1099    public IAsyncEnumerable<TunerHostInfo> DiscoverTuners([FromQuery] bool newDevicesOnly = false)
 01100        => _tunerHostManager.DiscoverTuners(newDevicesOnly);
 1101
 1102    /// <summary>
 1103    /// Gets a live tv recording stream.
 1104    /// </summary>
 1105    /// <param name="recordingId">Recording id.</param>
 1106    /// <response code="200">Recording stream returned.</response>
 1107    /// <response code="404">Recording not found.</response>
 1108    /// <returns>
 1109    /// An <see cref="OkResult"/> containing the recording stream on success,
 1110    /// or a <see cref="NotFoundResult"/> if recording not found.
 1111    /// </returns>
 1112    [HttpGet("LiveRecordings/{recordingId}/stream")]
 1113    [ProducesResponseType(StatusCodes.Status200OK)]
 1114    [ProducesResponseType(StatusCodes.Status404NotFound)]
 1115    [ProducesVideoFile]
 1116    public ActionResult GetLiveRecordingFile([FromRoute, Required] string recordingId)
 1117    {
 01118        var path = _recordingsManager.GetActiveRecordingPath(recordingId);
 01119        if (string.IsNullOrWhiteSpace(path))
 1120        {
 01121            return NotFound();
 1122        }
 1123
 01124        var stream = new ProgressiveFileStream(path, null, _transcodeManager);
 01125        return new FileStreamResult(stream, MimeTypes.GetMimeType(path));
 1126    }
 1127
 1128    /// <summary>
 1129    /// Gets a live tv channel stream.
 1130    /// </summary>
 1131    /// <param name="streamId">Stream id.</param>
 1132    /// <param name="container">Container type.</param>
 1133    /// <response code="200">Stream returned.</response>
 1134    /// <response code="404">Stream not found.</response>
 1135    /// <returns>
 1136    /// An <see cref="OkResult"/> containing the channel stream on success,
 1137    /// or a <see cref="NotFoundResult"/> if stream not found.
 1138    /// </returns>
 1139    [HttpGet("LiveStreamFiles/{streamId}/stream.{container}")]
 1140    [ProducesResponseType(StatusCodes.Status200OK)]
 1141    [ProducesResponseType(StatusCodes.Status404NotFound)]
 1142    [ProducesVideoFile]
 1143    public ActionResult GetLiveStreamFile(
 1144        [FromRoute, Required] string streamId,
 1145        [FromRoute, Required][RegularExpression(EncodingHelper.ContainerValidationRegexStr)] string container)
 1146    {
 01147        var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfoByUniqueId(streamId);
 01148        if (liveStreamInfo is null)
 1149        {
 01150            return NotFound();
 1151        }
 1152
 01153        var liveStream = new ProgressiveFileStream(liveStreamInfo.GetStream());
 01154        return new FileStreamResult(liveStream, MimeTypes.GetMimeType("file." + container));
 1155    }
 1156}

Methods/Properties

.ctor(MediaBrowser.Controller.LiveTv.ILiveTvManager,MediaBrowser.Controller.LiveTv.IGuideManager,MediaBrowser.Controller.LiveTv.ITunerHostManager,MediaBrowser.Controller.LiveTv.IListingsManager,MediaBrowser.Controller.LiveTv.IRecordingsManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Dto.IDtoService,MediaBrowser.Controller.Library.IMediaSourceManager,MediaBrowser.Controller.MediaEncoding.ITranscodeManager,MediaBrowser.Controller.LiveTv.ISchedulesDirectService)
GetLiveTvInfo()
GetLiveTvChannels(System.Nullable`1<MediaBrowser.Model.LiveTv.ChannelType>,System.Nullable`1<System.Guid>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Int32>,MediaBrowser.Model.Entities.ImageType[],MediaBrowser.Model.Querying.ItemFields[],System.Nullable`1<System.Boolean>,Jellyfin.Data.Enums.ItemSortBy[],System.Nullable`1<Jellyfin.Database.Implementations.Enums.SortOrder>,System.Boolean,System.Boolean)
GetChannel(System.Guid,System.Nullable`1<System.Guid>)
GetRecordings()
GetRecordingFolders()
GetRecording(System.Guid,System.Nullable`1<System.Guid>)
ResetTuner()
GetTimer()
GetDefaultTimer()
GetTimers()
GetLiveTvPrograms()
GetPrograms()
GetRecommendedPrograms()
GetProgram()
DeleteRecording(System.Guid)
CancelTimer()
UpdateTimer()
CreateTimer()
GetSeriesTimer()
GetSeriesTimers()
CancelSeriesTimer()
UpdateSeriesTimer()
CreateSeriesTimer()
GetGuideInfo()
AddTunerHost()
DeleteTunerHost(System.String)
GetDefaultListingProvider()
AddListingProvider()
DeleteListingProvider(System.String)
GetLineups()
GetSchedulesDirectCountries()
GetChannelMappingOptions(System.String)
SetChannelMapping(Jellyfin.Api.Models.LiveTvDtos.SetChannelMappingDto)
GetTunerHostTypes()
DiscoverTuners(System.Boolean)
GetLiveRecordingFile(System.String)
GetLiveStreamFile(System.String,System.String)