From f35774170fedd24697604923bd0d516b5dad9cd2 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 21 Jun 2020 16:12:21 -0600 Subject: Move LiveTvService.cs to Jellyfin.Api --- Jellyfin.Api/Controllers/LiveTvController.cs | 1151 ++++++++++++++++++++++++++ 1 file changed, 1151 insertions(+) create mode 100644 Jellyfin.Api/Controllers/LiveTvController.cs (limited to 'Jellyfin.Api/Controllers/LiveTvController.cs') diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs new file mode 100644 index 000000000..1279d4299 --- /dev/null +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -0,0 +1,1151 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net.Mime; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; +using Jellyfin.Api.Models.LiveTvDtos; +using Jellyfin.Data.Enums; +using MediaBrowser.Common; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.Querying; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers +{ + /// + /// Live tv controller. + /// + public class LiveTvController : BaseJellyfinApiController + { + private readonly ILiveTvManager _liveTvManager; + private readonly IUserManager _userManager; + private readonly IHttpClient _httpClient; + private readonly ILibraryManager _libraryManager; + private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; + private readonly ISessionContext _sessionContext; + private readonly IStreamHelper _streamHelper; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly IConfigurationManager _configurationManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public LiveTvController( + ILiveTvManager liveTvManager, + IUserManager userManager, + IHttpClient httpClient, + ILibraryManager libraryManager, + IDtoService dtoService, + IAuthorizationContext authContext, + ISessionContext sessionContext, + IStreamHelper streamHelper, + IMediaSourceManager mediaSourceManager, + IConfigurationManager configurationManager) + { + _liveTvManager = liveTvManager; + _userManager = userManager; + _httpClient = httpClient; + _libraryManager = libraryManager; + _dtoService = dtoService; + _authContext = authContext; + _sessionContext = sessionContext; + _streamHelper = streamHelper; + _mediaSourceManager = mediaSourceManager; + _configurationManager = configurationManager; + } + + /// + /// Gets available live tv services. + /// + /// Available live tv services returned. + /// + /// An containing the available live tv services. + /// + [HttpGet("Info")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult GetLiveTvInfo() + { + return _liveTvManager.GetLiveTvInfo(CancellationToken.None); + } + + /// + /// Gets available live tv channels. + /// + /// Optional. Filter by channel type. + /// Optional. Filter by user and attach user data. + /// Optional. The record index to start at. All items with a lower index will be dropped from the results. + /// Optional. Filter for movies. + /// Optional. Filter for series. + /// Optional. Filter for news. + /// Optional. Filter for kids. + /// Optional. Filter for sports. + /// Optional. The maximum number of records to return. + /// Optional. Filter by channels that are favorites, or not. + /// Optional. Filter by channels that are liked, or not. + /// Optional. Filter by channels that are disliked, or not. + /// Optional. Incorporate favorite and like status into channel sorting. + /// Optional. Include image information in output. + /// Optional. The max number of images to return, per image type. + /// "Optional. The image types to include in the output. + /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. + /// Optional. Include user data. + /// Optional. Key to sort by. + /// Optional. Sort order. + /// Optional. Adds current program info to each channel. + /// Available live tv channels returned. + /// + /// An containing the resulting available live tv channels. + /// + [HttpGet("Channels")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult> GetChannels( + [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 enableFavoriteSorting, + [FromQuery] bool? enableImages, + [FromQuery] int? imageTypeLimit, + [FromQuery] string enableImageTypes, + [FromQuery] string fields, + [FromQuery] bool? enableUserData, + [FromQuery] string sortBy, + [FromQuery] SortOrder? sortOrder, + [FromQuery] bool addCurrentProgram = true) + { + var dtoOptions = new DtoOptions() + .AddItemFields(fields) + .AddClientFields(Request) + .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); + + var channelResult = _liveTvManager.GetInternalChannels( + new LiveTvChannelQuery + { + ChannelType = type, + UserId = userId, + StartIndex = startIndex, + Limit = limit, + IsFavorite = isFavorite, + IsLiked = isLiked, + IsDisliked = isDisliked, + EnableFavoriteSorting = enableFavoriteSorting, + IsMovie = isMovie, + IsSeries = isSeries, + IsNews = isNews, + IsKids = isKids, + IsSports = isSports, + SortBy = RequestHelpers.Split(sortBy, ',', true), + SortOrder = sortOrder ?? SortOrder.Ascending, + AddCurrentProgram = addCurrentProgram + }, + dtoOptions, + CancellationToken.None); + + var user = userId.Equals(Guid.Empty) + ? null + : _userManager.GetUserById(userId); + + 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 + { + Items = returnArray, + TotalRecordCount = channelResult.TotalRecordCount + }; + } + + /// + /// Gets a live tv channel. + /// + /// Channel id. + /// Optional. Attach user data. + /// Live tv channel returned. + /// An containing the live tv channel. + [HttpGet("Channels/{channelId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult GetChannel([FromRoute] Guid channelId, [FromQuery] Guid userId) + { + var user = _userManager.GetUserById(userId); + var item = channelId.Equals(Guid.Empty) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(channelId); + + var dtoOptions = new DtoOptions() + .AddClientFields(Request); + return _dtoService.GetBaseItemDto(item, dtoOptions, user); + } + + /// + /// Gets live tv recordings. + /// + /// Optional. Filter by channel id. + /// Optional. Filter by user and attach user data. + /// Optional. The record index to start at. All items with a lower index will be dropped from the results. + /// Optional. The maximum number of records to return. + /// Optional. Filter by recording status. + /// Optional. Filter by recordings that are in progress, or not. + /// Optional. Filter by recordings belonging to a series timer. + /// Optional. Include image information in output. + /// Optional. The max number of images to return, per image type. + /// Optional. The image types to include in the output. + /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. + /// Optional. Include user data. + /// Optional. Filter for movies. + /// Optional. Filter for series. + /// Optional. Filter for kids. + /// Optional. Filter for sports. + /// Optional. Filter for news. + /// Optional. Filter for is library item. + /// Optional. Return total record count. + /// Live tv recordings returned. + /// An containing the live tv recordings. + [HttpGet("Recordings")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult> 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] string enableImageTypes, + [FromQuery] string fields, + [FromQuery] bool? enableUserData, + [FromQuery] bool? isMovie, + [FromQuery] bool? isSeries, + [FromQuery] bool? isKids, + [FromQuery] bool? isSports, + [FromQuery] bool? isNews, + [FromQuery] bool? isLibraryItem, + [FromQuery] bool enableTotalRecordCount = true) + { + var dtoOptions = new DtoOptions() + .AddItemFields(fields) + .AddClientFields(Request) + .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); + + return _liveTvManager.GetRecordings( + new RecordingQuery + { + ChannelId = channelId, + UserId = userId, + 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 = RequestHelpers.GetItemFields(fields), + ImageTypeLimit = imageTypeLimit, + EnableImages = enableImages + }, dtoOptions); + } + + /// + /// Gets live tv recording series. + /// + /// Optional. Filter by channel id. + /// Optional. Filter by user and attach user data. + /// Optional. Filter by recording group. + /// Optional. The record index to start at. All items with a lower index will be dropped from the results. + /// Optional. The maximum number of records to return. + /// Optional. Filter by recording status. + /// Optional. Filter by recordings that are in progress, or not. + /// Optional. Filter by recordings belonging to a series timer. + /// Optional. Include image information in output. + /// Optional. The max number of images to return, per image type. + /// Optional. The image types to include in the output. + /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. + /// Optional. Include user data. + /// Optional. Return total record count. + /// Live tv recordings returned. + /// An containing the live tv recordings. + [HttpGet("Recordings/Series")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + [Obsolete("This endpoint is obsolete.")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "channelId", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "groupId", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "startIndex", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "limit", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "status", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "isInProgress", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "seriesTimerId", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableImages", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageTypeLimit", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableImageTypes", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "fields", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableUserData", Justification = "Imported from ServiceStack")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableTotalRecordCount", Justification = "Imported from ServiceStack")] + public ActionResult> 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] string enableImageTypes, + [FromQuery] string fields, + [FromQuery] bool? enableUserData, + [FromQuery] bool enableTotalRecordCount = true) + { + return new QueryResult(); + } + + /// + /// Gets live tv recording groups. + /// + /// Optional. Filter by user and attach user data. + /// Recording groups returned. + /// An containing the recording groups. + [HttpGet("Recordings/Groups")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + [Obsolete("This endpoint is obsolete.")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")] + public ActionResult> GetRecordingGroups([FromQuery] Guid userId) + { + return new QueryResult(); + } + + /// + /// Gets recording folders. + /// + /// Optional. Filter by user and attach user data. + /// Recording folders returned. + /// An containing the recording folders. + [HttpGet("Recordings/Folders")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult> GetRecordingFolders([FromQuery] Guid userId) + { + var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId); + var folders = _liveTvManager.GetRecordingFolders(user); + + var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user); + + return new QueryResult + { + Items = returnArray, + TotalRecordCount = returnArray.Count + }; + } + + /// + /// Gets a live tv recording. + /// + /// Recording id. + /// Optional. Attach user data. + /// Recording returned. + /// An containing the live tv recording. + [HttpGet("Recordings/{recordingId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult GetRecording([FromRoute] Guid recordingId, [FromQuery] Guid userId) + { + var user = _userManager.GetUserById(userId); + var item = recordingId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId); + + var dtoOptions = new DtoOptions() + .AddClientFields(Request); + + return _dtoService.GetBaseItemDto(item, dtoOptions, user); + } + + /// + /// Resets a tv tuner. + /// + /// Tuner id. + /// Tuner reset. + /// A . + [HttpPost("Tuners/{tunerId}/Reset")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public ActionResult ResetTuner([FromRoute] string tunerId) + { + AssertUserCanManageLiveTv(); + _liveTvManager.ResetTuner(tunerId, CancellationToken.None); + return NoContent(); + } + + /// + /// Gets a timer. + /// + /// Timer id. + /// Timer returned. + /// + /// A containing an which contains the timer. + /// + [HttpGet("Timers/{timerId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public async Task> GetTimer(string timerId) + { + return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Gets the default values for a new timer. + /// + /// Optional. To attach default values based on a program. + /// Default values returned. + /// + /// A containing an which contains the default values for a timer. + /// + [HttpGet("Timers/Defaults")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public async Task> GetDefaultTimer([FromQuery] string programId) + { + return string.IsNullOrEmpty(programId) + ? await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false) + : await _liveTvManager.GetNewTimerDefaults(programId, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Gets the live tv timers. + /// + /// Optional. Filter by channel id. + /// Optional. Filter by timers belonging to a series timer. + /// Optional. Filter by timers that are active. + /// Optional. Filter by timers that are scheduled. + /// + /// A containing an which contains the live tv timers. + /// + [HttpGet("Timers")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public async Task>> 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); + } + + /// + /// Gets available live tv epgs. + /// + /// The channels to return guide information for. + /// Optional. Filter by user id. + /// Optional. The minimum premiere start date. + /// Optional. Filter by programs that have completed airing, or not. + /// Optional. Filter by programs that are currently airing, or not. + /// Optional. The maximum premiere start date. + /// Optional. The minimum premiere end date. + /// Optional. The maximum premiere end date. + /// Optional. Filter for movies. + /// Optional. Filter for series. + /// Optional. Filter for news. + /// Optional. Filter for kids. + /// Optional. Filter for sports. + /// Optional. The record index to start at. All items with a lower index will be dropped from the results. + /// Optional. The maximum number of records to return. + /// Optional. Specify one or more sort orders, comma delimited. Options: Name, StartDate. + /// Sort Order - Ascending,Descending. + /// The genres to return guide information for. + /// The genre ids to return guide information for. + /// Optional. Include image information in output. + /// Optional. The max number of images to return, per image type. + /// Optional. The image types to include in the output. + /// Optional. Include user data. + /// Optional. Filter by series timer id. + /// Optional. Filter by library series id. + /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. + /// Retrieve total record count. + /// Live tv epgs returned. + /// + /// A containing a which contains the live tv epgs. + /// + [HttpGet("Programs")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public async Task>> GetPrograms( + [FromQuery] string 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] string sortBy, + [FromQuery] string sortOrder, + [FromQuery] string genres, + [FromQuery] string genreIds, + [FromQuery] bool? enableImages, + [FromQuery] int? imageTypeLimit, + [FromQuery] string enableImageTypes, + [FromQuery] bool? enableUserData, + [FromQuery] string seriesTimerId, + [FromQuery] Guid librarySeriesId, + [FromQuery] string fields, + [FromQuery] bool enableTotalRecordCount = true) + { + var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId); + + var query = new InternalItemsQuery(user) + { + ChannelIds = RequestHelpers.Split(channelIds, ',', true) + .Select(i => new Guid(i)).ToArray(), + 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 = RequestHelpers.Split(genres, ',', true), + GenreIds = RequestHelpers.GetGuids(genreIds) + }; + + if (!librarySeriesId.Equals(Guid.Empty)) + { + query.IsSeries = true; + + if (_libraryManager.GetItemById(librarySeriesId) is Series series) + { + query.Name = series.Name; + } + } + + var dtoOptions = new DtoOptions() + .AddItemFields(fields) + .AddClientFields(Request) + .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); + return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Gets available live tv epgs. + /// + /// Request body. + /// Live tv epgs returned. + /// + /// A containing a which contains the live tv epgs. + /// + [HttpPost("Programs")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = Policies.DefaultAuthorization)] + public async Task>> GetPrograms([FromBody] GetProgramsDto body) + { + var user = body.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(body.UserId); + + var query = new InternalItemsQuery(user) + { + ChannelIds = RequestHelpers.Split(body.ChannelIds, ',', true) + .Select(i => new Guid(i)).ToArray(), + 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 = RequestHelpers.Split(body.Genres, ',', true), + GenreIds = RequestHelpers.GetGuids(body.GenreIds) + }; + + if (!body.LibrarySeriesId.Equals(Guid.Empty)) + { + query.IsSeries = true; + + if (_libraryManager.GetItemById(body.LibrarySeriesId) is Series series) + { + query.Name = series.Name; + } + } + + var dtoOptions = new DtoOptions() + .AddItemFields(body.Fields) + .AddClientFields(Request) + .AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes); + return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Gets recommended live tv epgs. + /// + /// Optional. filter by user id. + /// Optional. The maximum number of records to return. + /// Optional. Filter by programs that are currently airing, or not. + /// Optional. Filter by programs that have completed airing, or not. + /// Optional. Filter for series. + /// Optional. Filter for movies. + /// Optional. Filter for news. + /// Optional. Filter for kids. + /// Optional. Filter for sports. + /// Optional. Include image information in output. + /// Optional. The max number of images to return, per image type. + /// Optional. The image types to include in the output. + /// The genres to return guide information for. + /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. + /// Optional. include user data. + /// Retrieve total record count. + /// Recommended epgs returned. + /// A containing the queryresult of recommended epgs. + [HttpGet("Programs/Recommended")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> 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] string enableImageTypes, + [FromQuery] string genreIds, + [FromQuery] string fields, + [FromQuery] bool? enableUserData, + [FromQuery] bool enableTotalRecordCount = true) + { + var user = _userManager.GetUserById(userId); + + var query = new InternalItemsQuery(user) + { + IsAiring = isAiring, + Limit = limit, + HasAired = hasAired, + IsSeries = isSeries, + IsMovie = isMovie, + IsKids = isKids, + IsNews = isNews, + IsSports = isSports, + EnableTotalRecordCount = enableTotalRecordCount, + GenreIds = RequestHelpers.GetGuids(genreIds) + }; + + var dtoOptions = new DtoOptions() + .AddItemFields(fields) + .AddClientFields(Request) + .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); + return _liveTvManager.GetRecommendedPrograms(query, dtoOptions, CancellationToken.None); + } + + /// + /// Deletes a live tv recording. + /// + /// Recording id. + /// Recording deleted. + /// Item not found. + /// A on success, or a if item not found. + [HttpDelete("Recordings/{recordingId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult DeleteRecording([FromRoute] Guid recordingId) + { + AssertUserCanManageLiveTv(); + + var item = _libraryManager.GetItemById(recordingId); + if (item == null) + { + return NotFound(); + } + + _libraryManager.DeleteItem(item, new DeleteOptions + { + DeleteFileLocation = false + }); + + return NoContent(); + } + + /// + /// Cancels a live tv timer. + /// + /// Timer id. + /// Timer deleted. + /// A . + [HttpDelete("Timers/{timerId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task CancelTimer([FromRoute] string timerId) + { + AssertUserCanManageLiveTv(); + await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false); + return NoContent(); + } + + /// + /// Updates a live tv timer. + /// + /// Timer id. + /// New timer info. + /// Timer updated. + /// A . + [HttpPost("Timers/{timerId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task UpdateTimer([FromRoute] string timerId, [FromBody] TimerInfoDto timerInfo) + { + AssertUserCanManageLiveTv(); + await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false); + return NoContent(); + } + + /// + /// Creates a live tv timer. + /// + /// New timer info. + /// Timer created. + /// A . + [HttpPost("Timers")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task CreateTimer([FromBody] TimerInfoDto timerInfo) + { + AssertUserCanManageLiveTv(); + await _liveTvManager.CreateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false); + return NoContent(); + } + + /// + /// Gets a live tv series timer. + /// + /// Timer id. + /// Series timer returned. + /// Series timer not found. + /// A on success, or a if timer not found. + [HttpGet("SeriesTimers/{timerId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> GetSeriesTimer([FromRoute] string timerId) + { + var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false); + if (timer == null) + { + return NotFound(); + } + + return timer; + } + + /// + /// Gets live tv series timers. + /// + /// Optional. Sort by SortName or Priority. + /// Optional. Sort in Ascending or Descending order. + /// Timers returned. + /// An of live tv series timers. + [HttpGet("SeriesTimers")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task>> GetSeriesTimers([FromQuery] string sortBy, [FromQuery] SortOrder sortOrder) + { + return await _liveTvManager.GetSeriesTimers( + new SeriesTimerQuery + { + SortOrder = sortOrder, + SortBy = sortBy + }, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Cancels a live tv series timer. + /// + /// Timer id. + /// Timer cancelled. + /// A . + [HttpDelete("SeriesTimers/{timerId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task CancelSeriesTimer([FromRoute] string timerId) + { + AssertUserCanManageLiveTv(); + await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false); + return NoContent(); + } + + /// + /// Updates a live tv series timer. + /// + /// Timer id. + /// New series timer info. + /// Series timer updated. + /// A . + [HttpPost("SeriesTimers/{timerId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task UpdateSeriesTimer([FromRoute] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) + { + AssertUserCanManageLiveTv(); + await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false); + return NoContent(); + } + + /// + /// Creates a live tv series timer. + /// + /// New series timer info. + /// Series timer info created. + /// A . + [HttpPost("SeriesTimers")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task CreateSeriesTimer([FromBody] SeriesTimerInfoDto seriesTimerInfo) + { + AssertUserCanManageLiveTv(); + await _liveTvManager.CreateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false); + return NoContent(); + } + + /// + /// Get recording group. + /// + /// Group id. + /// A . + [HttpGet("Recordings/Groups/{groupId}")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [Obsolete("This endpoint is obsolete.")] + public ActionResult GetRecordingGroup([FromQuery] Guid groupId) + { + return NotFound(); + } + + /// + /// Get guid info. + /// + /// Guid info returned. + /// An containing the guide info. + [HttpGet("GuideInfo")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetGuideInfo() + { + return _liveTvManager.GetGuideInfo(); + } + + /// + /// Adds a tuner host. + /// + /// New tuner host. + /// Created tuner host returned. + /// A containing the created tuner host. + [HttpPost("TunerHosts")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo) + { + return await _liveTvManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false); + } + + /// + /// Deletes a tuner host. + /// + /// Tuner host id. + /// Tuner host deleted. + /// A . + [HttpDelete("TunerHosts")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult DeleteTunerHost([FromQuery] string id) + { + var config = _configurationManager.GetConfiguration("livetv"); + config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray(); + _configurationManager.SaveConfiguration("livetv", config); + return NoContent(); + } + + /// + /// Gets default listings provider info. + /// + /// Default listings provider info returned. + /// An containing the default listings provider info. + [HttpGet("ListingProviders/Default")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetDefaultListingProvider() + { + return new ListingsProviderInfo(); + } + + /// + /// Adds a listings provider. + /// + /// Validate login. + /// Validate listings. + /// Password. + /// New listings info. + /// Created listings provider returned. + /// A containing the created listings provider. + [HttpGet("ListingProviders")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> AddListingProvider( + [FromQuery] bool validateLogin, + [FromQuery] bool validateListings, + [FromQuery] string pw, + [FromBody] ListingsProviderInfo listingsProviderInfo) + { + using var sha = SHA1.Create(); + if (!string.IsNullOrEmpty(pw)) + { + listingsProviderInfo.Password = Hex.Encode(sha.ComputeHash(Encoding.UTF8.GetBytes(pw))); + } + + return await _liveTvManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false); + } + + /// + /// Delete listing provider. + /// + /// Listing provider id. + /// Listing provider deleted. + /// A . + [HttpGet("ListingProviders")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult DeleteListingProvider([FromQuery] string id) + { + _liveTvManager.DeleteListingsProvider(id); + return NoContent(); + } + + /// + /// Gets available lineups. + /// + /// Provider id. + /// Provider type. + /// Location. + /// Country. + /// Available lineups returned. + /// A containing the available lineups. + [HttpGet("ListingProviders/Lineups")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task>> GetLineups( + [FromQuery] string id, + [FromQuery] string type, + [FromQuery] string location, + [FromQuery] string country) + { + return await _liveTvManager.GetLineups(type, id, country, location).ConfigureAwait(false); + } + + /// + /// Gets available countries. + /// + /// Available countries returned. + /// A containing the available countries. + [HttpGet("ListingProviders/SchedulesDirect/Countries")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task GetSchedulesDirectCountries() + { + // https://json.schedulesdirect.org/20141201/available/countries + var response = await _httpClient.Get(new HttpRequestOptions + { + Url = "https://json.schedulesdirect.org/20141201/available/countries", + BufferContent = false + }).ConfigureAwait(false); + return File(response, MediaTypeNames.Application.Json); + } + + /// + /// Get channel mapping options. + /// + /// Provider id. + /// Channel mapping options returned. + /// An containing the channel mapping options. + [HttpGet("ChannelMappingOptions")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetChannelMappingOptions([FromQuery] string providerId) + { + var config = _configurationManager.GetConfiguration("livetv"); + + var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase)); + + var listingsProviderName = _liveTvManager.ListingProviders.First(i => string.Equals(i.Type, listingsProviderInfo.Type, StringComparison.OrdinalIgnoreCase)).Name; + + var tunerChannels = await _liveTvManager.GetChannelsForListingsProvider(providerId, CancellationToken.None) + .ConfigureAwait(false); + + var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(providerId, CancellationToken.None) + .ConfigureAwait(false); + + var mappings = listingsProviderInfo.ChannelMappings; + + return new ChannelMappingOptionsDto + { + TunerChannels = tunerChannels.Select(i => _liveTvManager.GetTunerChannelMapping(i, mappings, providerChannels)).ToList(), + ProviderChannels = providerChannels.Select(i => new NameIdPair + { + Name = i.Name, + Id = i.Id + }).ToList(), + Mappings = mappings, + ProviderName = listingsProviderName + }; + } + + /// + /// Set channel mappings. + /// + /// Provider id. + /// Tuner channel id. + /// Provider channel id. + /// Created channel mapping returned. + /// An containing the created channel mapping. + [HttpPost("ChannelMappings")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> SetChannelMapping( + [FromQuery] string providerId, + [FromQuery] string tunerChannelId, + [FromQuery] string providerChannelId) + { + return await _liveTvManager.SetChannelMapping(providerId, tunerChannelId, providerChannelId).ConfigureAwait(false); + } + + /// + /// Get tuner host types. + /// + /// Tuner host types returned. + /// An containing the tuner host types. + [HttpGet("TunerHosts/Types")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetTunerHostTypes() + { + return _liveTvManager.GetTunerHostTypes(); + } + + /// + /// Discover tuners. + /// + /// Only discover new tuners. + /// Tuners returned. + /// An containing the tuners. + [HttpGet("Tuners/Discvover")] + [Authorize(Policy = Policies.DefaultAuthorization)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task>> DiscoverTuners([FromQuery] bool newDevicesOnly) + { + return await _liveTvManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false); + } + + private void AssertUserCanManageLiveTv() + { + var user = _sessionContext.GetUser(Request); + + if (user == null) + { + throw new SecurityException("Anonymous live tv management is not allowed."); + } + + if (!user.HasPermission(PermissionKind.EnableLiveTvManagement)) + { + throw new SecurityException("The current user does not have permission to manage live tv."); + } + } + } +} -- cgit v1.2.3