LiveTvController.cs 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Linq;
  6. using System.Net.Http;
  7. using System.Net.Mime;
  8. using System.Security.Cryptography;
  9. using System.Text;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using Jellyfin.Api.Attributes;
  13. using Jellyfin.Api.Extensions;
  14. using Jellyfin.Api.Helpers;
  15. using Jellyfin.Api.ModelBinders;
  16. using Jellyfin.Api.Models.LiveTvDtos;
  17. using Jellyfin.Data.Enums;
  18. using Jellyfin.Extensions;
  19. using MediaBrowser.Common.Api;
  20. using MediaBrowser.Common.Configuration;
  21. using MediaBrowser.Common.Net;
  22. using MediaBrowser.Controller.Dto;
  23. using MediaBrowser.Controller.Entities;
  24. using MediaBrowser.Controller.Entities.TV;
  25. using MediaBrowser.Controller.Library;
  26. using MediaBrowser.Controller.LiveTv;
  27. using MediaBrowser.Controller.MediaEncoding;
  28. using MediaBrowser.Controller.Streaming;
  29. using MediaBrowser.Model.Dto;
  30. using MediaBrowser.Model.Entities;
  31. using MediaBrowser.Model.LiveTv;
  32. using MediaBrowser.Model.Net;
  33. using MediaBrowser.Model.Querying;
  34. using Microsoft.AspNetCore.Authorization;
  35. using Microsoft.AspNetCore.Http;
  36. using Microsoft.AspNetCore.Mvc;
  37. namespace Jellyfin.Api.Controllers;
  38. /// <summary>
  39. /// Live tv controller.
  40. /// </summary>
  41. public 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 IHttpClientFactory _httpClientFactory;
  50. private readonly ILibraryManager _libraryManager;
  51. private readonly IDtoService _dtoService;
  52. private readonly IMediaSourceManager _mediaSourceManager;
  53. private readonly IConfigurationManager _configurationManager;
  54. private readonly ITranscodeManager _transcodeManager;
  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="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
  65. /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
  66. /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
  67. /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
  68. /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
  69. /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
  70. public LiveTvController(
  71. ILiveTvManager liveTvManager,
  72. IGuideManager guideManager,
  73. ITunerHostManager tunerHostManager,
  74. IListingsManager listingsManager,
  75. IRecordingsManager recordingsManager,
  76. IUserManager userManager,
  77. IHttpClientFactory httpClientFactory,
  78. ILibraryManager libraryManager,
  79. IDtoService dtoService,
  80. IMediaSourceManager mediaSourceManager,
  81. IConfigurationManager configurationManager,
  82. ITranscodeManager transcodeManager)
  83. {
  84. _liveTvManager = liveTvManager;
  85. _guideManager = guideManager;
  86. _tunerHostManager = tunerHostManager;
  87. _listingsManager = listingsManager;
  88. _recordingsManager = recordingsManager;
  89. _userManager = userManager;
  90. _httpClientFactory = httpClientFactory;
  91. _libraryManager = libraryManager;
  92. _dtoService = dtoService;
  93. _mediaSourceManager = mediaSourceManager;
  94. _configurationManager = configurationManager;
  95. _transcodeManager = transcodeManager;
  96. }
  97. /// <summary>
  98. /// Gets available live tv services.
  99. /// </summary>
  100. /// <response code="200">Available live tv services returned.</response>
  101. /// <returns>
  102. /// An <see cref="OkResult"/> containing the available live tv services.
  103. /// </returns>
  104. [HttpGet("Info")]
  105. [ProducesResponseType(StatusCodes.Status200OK)]
  106. [Authorize(Policy = Policies.LiveTvAccess)]
  107. public ActionResult<LiveTvInfo> GetLiveTvInfo()
  108. {
  109. return _liveTvManager.GetLiveTvInfo(CancellationToken.None);
  110. }
  111. /// <summary>
  112. /// Gets available live tv channels.
  113. /// </summary>
  114. /// <param name="type">Optional. Filter by channel type.</param>
  115. /// <param name="userId">Optional. Filter by user and attach user data.</param>
  116. /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
  117. /// <param name="isMovie">Optional. Filter for movies.</param>
  118. /// <param name="isSeries">Optional. Filter for series.</param>
  119. /// <param name="isNews">Optional. Filter for news.</param>
  120. /// <param name="isKids">Optional. Filter for kids.</param>
  121. /// <param name="isSports">Optional. Filter for sports.</param>
  122. /// <param name="limit">Optional. The maximum number of records to return.</param>
  123. /// <param name="isFavorite">Optional. Filter by channels that are favorites, or not.</param>
  124. /// <param name="isLiked">Optional. Filter by channels that are liked, or not.</param>
  125. /// <param name="isDisliked">Optional. Filter by channels that are disliked, or not.</param>
  126. /// <param name="enableImages">Optional. Include image information in output.</param>
  127. /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
  128. /// <param name="enableImageTypes">"Optional. The image types to include in the output.</param>
  129. /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
  130. /// <param name="enableUserData">Optional. Include user data.</param>
  131. /// <param name="sortBy">Optional. Key to sort by.</param>
  132. /// <param name="sortOrder">Optional. Sort order.</param>
  133. /// <param name="enableFavoriteSorting">Optional. Incorporate favorite and like status into channel sorting.</param>
  134. /// <param name="addCurrentProgram">Optional. Adds current program info to each channel.</param>
  135. /// <response code="200">Available live tv channels returned.</response>
  136. /// <returns>
  137. /// An <see cref="OkResult"/> containing the resulting available live tv channels.
  138. /// </returns>
  139. [HttpGet("Channels")]
  140. [ProducesResponseType(StatusCodes.Status200OK)]
  141. [Authorize(Policy = Policies.LiveTvAccess)]
  142. public ActionResult<QueryResult<BaseItemDto>> GetLiveTvChannels(
  143. [FromQuery] ChannelType? type,
  144. [FromQuery] Guid? userId,
  145. [FromQuery] int? startIndex,
  146. [FromQuery] bool? isMovie,
  147. [FromQuery] bool? isSeries,
  148. [FromQuery] bool? isNews,
  149. [FromQuery] bool? isKids,
  150. [FromQuery] bool? isSports,
  151. [FromQuery] int? limit,
  152. [FromQuery] bool? isFavorite,
  153. [FromQuery] bool? isLiked,
  154. [FromQuery] bool? isDisliked,
  155. [FromQuery] bool? enableImages,
  156. [FromQuery] int? imageTypeLimit,
  157. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
  158. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
  159. [FromQuery] bool? enableUserData,
  160. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
  161. [FromQuery] SortOrder? sortOrder,
  162. [FromQuery] bool enableFavoriteSorting = false,
  163. [FromQuery] bool addCurrentProgram = true)
  164. {
  165. userId = RequestHelpers.GetUserId(User, userId);
  166. var dtoOptions = new DtoOptions { Fields = fields }
  167. .AddClientFields(User)
  168. .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
  169. var channelResult = _liveTvManager.GetInternalChannels(
  170. new LiveTvChannelQuery
  171. {
  172. ChannelType = type,
  173. UserId = userId.Value,
  174. StartIndex = startIndex,
  175. Limit = limit,
  176. IsFavorite = isFavorite,
  177. IsLiked = isLiked,
  178. IsDisliked = isDisliked,
  179. EnableFavoriteSorting = enableFavoriteSorting,
  180. IsMovie = isMovie,
  181. IsSeries = isSeries,
  182. IsNews = isNews,
  183. IsKids = isKids,
  184. IsSports = isSports,
  185. SortBy = sortBy,
  186. SortOrder = sortOrder ?? SortOrder.Ascending,
  187. AddCurrentProgram = addCurrentProgram
  188. },
  189. dtoOptions,
  190. CancellationToken.None);
  191. var user = userId.IsNullOrEmpty()
  192. ? null
  193. : _userManager.GetUserById(userId.Value);
  194. var fieldsList = dtoOptions.Fields.ToList();
  195. fieldsList.Remove(ItemFields.CanDelete);
  196. fieldsList.Remove(ItemFields.CanDownload);
  197. fieldsList.Remove(ItemFields.DisplayPreferencesId);
  198. fieldsList.Remove(ItemFields.Etag);
  199. dtoOptions.Fields = fieldsList.ToArray();
  200. dtoOptions.AddCurrentProgram = addCurrentProgram;
  201. var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, dtoOptions, user);
  202. return new QueryResult<BaseItemDto>(
  203. startIndex,
  204. channelResult.TotalRecordCount,
  205. returnArray);
  206. }
  207. /// <summary>
  208. /// Gets a live tv channel.
  209. /// </summary>
  210. /// <param name="channelId">Channel id.</param>
  211. /// <param name="userId">Optional. Attach user data.</param>
  212. /// <response code="200">Live tv channel returned.</response>
  213. /// <response code="404">Item not found.</response>
  214. /// <returns>An <see cref="OkResult"/> containing the live tv channel.</returns>
  215. [HttpGet("Channels/{channelId}")]
  216. [ProducesResponseType(StatusCodes.Status200OK)]
  217. [ProducesResponseType(StatusCodes.Status404NotFound)]
  218. [Authorize(Policy = Policies.LiveTvAccess)]
  219. public ActionResult<BaseItemDto> GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId)
  220. {
  221. userId = RequestHelpers.GetUserId(User, userId);
  222. var user = userId.IsNullOrEmpty()
  223. ? null
  224. : _userManager.GetUserById(userId.Value);
  225. var item = channelId.IsEmpty()
  226. ? _libraryManager.GetUserRootFolder()
  227. : _libraryManager.GetItemById<BaseItem>(channelId, user);
  228. if (item is null)
  229. {
  230. return NotFound();
  231. }
  232. var dtoOptions = new DtoOptions()
  233. .AddClientFields(User);
  234. return _dtoService.GetBaseItemDto(item, dtoOptions, user);
  235. }
  236. /// <summary>
  237. /// Gets live tv recordings.
  238. /// </summary>
  239. /// <param name="channelId">Optional. Filter by channel id.</param>
  240. /// <param name="userId">Optional. Filter by user and attach user data.</param>
  241. /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
  242. /// <param name="limit">Optional. The maximum number of records to return.</param>
  243. /// <param name="status">Optional. Filter by recording status.</param>
  244. /// <param name="isInProgress">Optional. Filter by recordings that are in progress, or not.</param>
  245. /// <param name="seriesTimerId">Optional. Filter by recordings belonging to a series timer.</param>
  246. /// <param name="enableImages">Optional. Include image information in output.</param>
  247. /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
  248. /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
  249. /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
  250. /// <param name="enableUserData">Optional. Include user data.</param>
  251. /// <param name="isMovie">Optional. Filter for movies.</param>
  252. /// <param name="isSeries">Optional. Filter for series.</param>
  253. /// <param name="isKids">Optional. Filter for kids.</param>
  254. /// <param name="isSports">Optional. Filter for sports.</param>
  255. /// <param name="isNews">Optional. Filter for news.</param>
  256. /// <param name="isLibraryItem">Optional. Filter for is library item.</param>
  257. /// <param name="enableTotalRecordCount">Optional. Return total record count.</param>
  258. /// <response code="200">Live tv recordings returned.</response>
  259. /// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
  260. [HttpGet("Recordings")]
  261. [ProducesResponseType(StatusCodes.Status200OK)]
  262. [Authorize(Policy = Policies.LiveTvAccess)]
  263. public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecordings(
  264. [FromQuery] string? channelId,
  265. [FromQuery] Guid? userId,
  266. [FromQuery] int? startIndex,
  267. [FromQuery] int? limit,
  268. [FromQuery] RecordingStatus? status,
  269. [FromQuery] bool? isInProgress,
  270. [FromQuery] string? seriesTimerId,
  271. [FromQuery] bool? enableImages,
  272. [FromQuery] int? imageTypeLimit,
  273. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
  274. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
  275. [FromQuery] bool? enableUserData,
  276. [FromQuery] bool? isMovie,
  277. [FromQuery] bool? isSeries,
  278. [FromQuery] bool? isKids,
  279. [FromQuery] bool? isSports,
  280. [FromQuery] bool? isNews,
  281. [FromQuery] bool? isLibraryItem,
  282. [FromQuery] bool enableTotalRecordCount = true)
  283. {
  284. userId = RequestHelpers.GetUserId(User, userId);
  285. var dtoOptions = new DtoOptions { Fields = fields }
  286. .AddClientFields(User)
  287. .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
  288. return await _liveTvManager.GetRecordingsAsync(
  289. new RecordingQuery
  290. {
  291. ChannelId = channelId,
  292. UserId = userId.Value,
  293. StartIndex = startIndex,
  294. Limit = limit,
  295. Status = status,
  296. SeriesTimerId = seriesTimerId,
  297. IsInProgress = isInProgress,
  298. EnableTotalRecordCount = enableTotalRecordCount,
  299. IsMovie = isMovie,
  300. IsNews = isNews,
  301. IsSeries = isSeries,
  302. IsKids = isKids,
  303. IsSports = isSports,
  304. IsLibraryItem = isLibraryItem,
  305. Fields = fields,
  306. ImageTypeLimit = imageTypeLimit,
  307. EnableImages = enableImages
  308. },
  309. dtoOptions).ConfigureAwait(false);
  310. }
  311. /// <summary>
  312. /// Gets live tv recording series.
  313. /// </summary>
  314. /// <param name="channelId">Optional. Filter by channel id.</param>
  315. /// <param name="userId">Optional. Filter by user and attach user data.</param>
  316. /// <param name="groupId">Optional. Filter by recording group.</param>
  317. /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
  318. /// <param name="limit">Optional. The maximum number of records to return.</param>
  319. /// <param name="status">Optional. Filter by recording status.</param>
  320. /// <param name="isInProgress">Optional. Filter by recordings that are in progress, or not.</param>
  321. /// <param name="seriesTimerId">Optional. Filter by recordings belonging to a series timer.</param>
  322. /// <param name="enableImages">Optional. Include image information in output.</param>
  323. /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
  324. /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
  325. /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
  326. /// <param name="enableUserData">Optional. Include user data.</param>
  327. /// <param name="enableTotalRecordCount">Optional. Return total record count.</param>
  328. /// <response code="200">Live tv recordings returned.</response>
  329. /// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
  330. [HttpGet("Recordings/Series")]
  331. [ProducesResponseType(StatusCodes.Status200OK)]
  332. [Authorize(Policy = Policies.LiveTvAccess)]
  333. [Obsolete("This endpoint is obsolete.")]
  334. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "channelId", Justification = "Imported from ServiceStack")]
  335. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")]
  336. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "groupId", Justification = "Imported from ServiceStack")]
  337. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "startIndex", Justification = "Imported from ServiceStack")]
  338. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "limit", Justification = "Imported from ServiceStack")]
  339. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "status", Justification = "Imported from ServiceStack")]
  340. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "isInProgress", Justification = "Imported from ServiceStack")]
  341. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "seriesTimerId", Justification = "Imported from ServiceStack")]
  342. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableImages", Justification = "Imported from ServiceStack")]
  343. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageTypeLimit", Justification = "Imported from ServiceStack")]
  344. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableImageTypes", Justification = "Imported from ServiceStack")]
  345. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "fields", Justification = "Imported from ServiceStack")]
  346. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableUserData", Justification = "Imported from ServiceStack")]
  347. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableTotalRecordCount", Justification = "Imported from ServiceStack")]
  348. public ActionResult<QueryResult<BaseItemDto>> GetRecordingsSeries(
  349. [FromQuery] string? channelId,
  350. [FromQuery] Guid? userId,
  351. [FromQuery] string? groupId,
  352. [FromQuery] int? startIndex,
  353. [FromQuery] int? limit,
  354. [FromQuery] RecordingStatus? status,
  355. [FromQuery] bool? isInProgress,
  356. [FromQuery] string? seriesTimerId,
  357. [FromQuery] bool? enableImages,
  358. [FromQuery] int? imageTypeLimit,
  359. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
  360. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
  361. [FromQuery] bool? enableUserData,
  362. [FromQuery] bool enableTotalRecordCount = true)
  363. {
  364. return new QueryResult<BaseItemDto>();
  365. }
  366. /// <summary>
  367. /// Gets live tv recording groups.
  368. /// </summary>
  369. /// <param name="userId">Optional. Filter by user and attach user data.</param>
  370. /// <response code="200">Recording groups returned.</response>
  371. /// <returns>An <see cref="OkResult"/> containing the recording groups.</returns>
  372. [HttpGet("Recordings/Groups")]
  373. [ProducesResponseType(StatusCodes.Status200OK)]
  374. [Authorize(Policy = Policies.LiveTvAccess)]
  375. [Obsolete("This endpoint is obsolete.")]
  376. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")]
  377. public ActionResult<QueryResult<BaseItemDto>> GetRecordingGroups([FromQuery] Guid? userId)
  378. {
  379. return new QueryResult<BaseItemDto>();
  380. }
  381. /// <summary>
  382. /// Gets recording folders.
  383. /// </summary>
  384. /// <param name="userId">Optional. Filter by user and attach user data.</param>
  385. /// <response code="200">Recording folders returned.</response>
  386. /// <returns>An <see cref="OkResult"/> containing the recording folders.</returns>
  387. [HttpGet("Recordings/Folders")]
  388. [ProducesResponseType(StatusCodes.Status200OK)]
  389. [Authorize(Policy = Policies.LiveTvAccess)]
  390. public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecordingFolders([FromQuery] Guid? userId)
  391. {
  392. userId = RequestHelpers.GetUserId(User, userId);
  393. var user = userId.IsNullOrEmpty()
  394. ? null
  395. : _userManager.GetUserById(userId.Value);
  396. var folders = await _liveTvManager.GetRecordingFoldersAsync(user).ConfigureAwait(false);
  397. var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user);
  398. return new QueryResult<BaseItemDto>(returnArray);
  399. }
  400. /// <summary>
  401. /// Gets a live tv recording.
  402. /// </summary>
  403. /// <param name="recordingId">Recording id.</param>
  404. /// <param name="userId">Optional. Attach user data.</param>
  405. /// <response code="200">Recording returned.</response>
  406. /// <response code="404">Item not found.</response>
  407. /// <returns>An <see cref="OkResult"/> containing the live tv recording.</returns>
  408. [HttpGet("Recordings/{recordingId}")]
  409. [ProducesResponseType(StatusCodes.Status200OK)]
  410. [ProducesResponseType(StatusCodes.Status404NotFound)]
  411. [Authorize(Policy = Policies.LiveTvAccess)]
  412. public ActionResult<BaseItemDto> GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId)
  413. {
  414. userId = RequestHelpers.GetUserId(User, userId);
  415. var user = userId.IsNullOrEmpty()
  416. ? null
  417. : _userManager.GetUserById(userId.Value);
  418. var item = recordingId.IsEmpty()
  419. ? _libraryManager.GetUserRootFolder()
  420. : _libraryManager.GetItemById<BaseItem>(recordingId, user);
  421. if (item is null)
  422. {
  423. return NotFound();
  424. }
  425. var dtoOptions = new DtoOptions()
  426. .AddClientFields(User);
  427. return _dtoService.GetBaseItemDto(item, dtoOptions, user);
  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.LiveTvManagement)]
  438. public async Task<ActionResult> ResetTuner([FromRoute, Required] string tunerId)
  439. {
  440. await _liveTvManager.ResetTuner(tunerId, CancellationToken.None).ConfigureAwait(false);
  441. return NoContent();
  442. }
  443. /// <summary>
  444. /// Gets a timer.
  445. /// </summary>
  446. /// <param name="timerId">Timer id.</param>
  447. /// <response code="200">Timer returned.</response>
  448. /// <returns>
  449. /// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the timer.
  450. /// </returns>
  451. [HttpGet("Timers/{timerId}")]
  452. [ProducesResponseType(StatusCodes.Status200OK)]
  453. [Authorize(Policy = Policies.LiveTvAccess)]
  454. public async Task<ActionResult<TimerInfoDto>> GetTimer([FromRoute, Required] string timerId)
  455. {
  456. return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false);
  457. }
  458. /// <summary>
  459. /// Gets the default values for a new timer.
  460. /// </summary>
  461. /// <param name="programId">Optional. To attach default values based on a program.</param>
  462. /// <response code="200">Default values returned.</response>
  463. /// <returns>
  464. /// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the default values for a timer.
  465. /// </returns>
  466. [HttpGet("Timers/Defaults")]
  467. [ProducesResponseType(StatusCodes.Status200OK)]
  468. [Authorize(Policy = Policies.LiveTvAccess)]
  469. public async Task<ActionResult<SeriesTimerInfoDto>> GetDefaultTimer([FromQuery] string? programId)
  470. {
  471. return string.IsNullOrEmpty(programId)
  472. ? await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false)
  473. : await _liveTvManager.GetNewTimerDefaults(programId, CancellationToken.None).ConfigureAwait(false);
  474. }
  475. /// <summary>
  476. /// Gets the live tv timers.
  477. /// </summary>
  478. /// <param name="channelId">Optional. Filter by channel id.</param>
  479. /// <param name="seriesTimerId">Optional. Filter by timers belonging to a series timer.</param>
  480. /// <param name="isActive">Optional. Filter by timers that are active.</param>
  481. /// <param name="isScheduled">Optional. Filter by timers that are scheduled.</param>
  482. /// <returns>
  483. /// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the live tv timers.
  484. /// </returns>
  485. [HttpGet("Timers")]
  486. [ProducesResponseType(StatusCodes.Status200OK)]
  487. [Authorize(Policy = Policies.LiveTvAccess)]
  488. public async Task<ActionResult<QueryResult<TimerInfoDto>>> GetTimers(
  489. [FromQuery] string? channelId,
  490. [FromQuery] string? seriesTimerId,
  491. [FromQuery] bool? isActive,
  492. [FromQuery] bool? isScheduled)
  493. {
  494. return await _liveTvManager.GetTimers(
  495. new TimerQuery
  496. {
  497. ChannelId = channelId,
  498. SeriesTimerId = seriesTimerId,
  499. IsActive = isActive,
  500. IsScheduled = isScheduled
  501. },
  502. CancellationToken.None).ConfigureAwait(false);
  503. }
  504. /// <summary>
  505. /// Gets available live tv epgs.
  506. /// </summary>
  507. /// <param name="channelIds">The channels to return guide information for.</param>
  508. /// <param name="userId">Optional. Filter by user id.</param>
  509. /// <param name="minStartDate">Optional. The minimum premiere start date.</param>
  510. /// <param name="hasAired">Optional. Filter by programs that have completed airing, or not.</param>
  511. /// <param name="isAiring">Optional. Filter by programs that are currently airing, or not.</param>
  512. /// <param name="maxStartDate">Optional. The maximum premiere start date.</param>
  513. /// <param name="minEndDate">Optional. The minimum premiere end date.</param>
  514. /// <param name="maxEndDate">Optional. The maximum premiere end date.</param>
  515. /// <param name="isMovie">Optional. Filter for movies.</param>
  516. /// <param name="isSeries">Optional. Filter for series.</param>
  517. /// <param name="isNews">Optional. Filter for news.</param>
  518. /// <param name="isKids">Optional. Filter for kids.</param>
  519. /// <param name="isSports">Optional. Filter for sports.</param>
  520. /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
  521. /// <param name="limit">Optional. The maximum number of records to return.</param>
  522. /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Name, StartDate.</param>
  523. /// <param name="sortOrder">Sort Order - Ascending,Descending.</param>
  524. /// <param name="genres">The genres to return guide information for.</param>
  525. /// <param name="genreIds">The genre ids to return guide information for.</param>
  526. /// <param name="enableImages">Optional. Include image information in output.</param>
  527. /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
  528. /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
  529. /// <param name="enableUserData">Optional. Include user data.</param>
  530. /// <param name="seriesTimerId">Optional. Filter by series timer id.</param>
  531. /// <param name="librarySeriesId">Optional. Filter by library series id.</param>
  532. /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
  533. /// <param name="enableTotalRecordCount">Retrieve total record count.</param>
  534. /// <response code="200">Live tv epgs returned.</response>
  535. /// <returns>
  536. /// A <see cref="Task"/> containing a <see cref="OkResult"/> which contains the live tv epgs.
  537. /// </returns>
  538. [HttpGet("Programs")]
  539. [ProducesResponseType(StatusCodes.Status200OK)]
  540. [Authorize(Policy = Policies.LiveTvAccess)]
  541. public async Task<ActionResult<QueryResult<BaseItemDto>>> GetLiveTvPrograms(
  542. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds,
  543. [FromQuery] Guid? userId,
  544. [FromQuery] DateTime? minStartDate,
  545. [FromQuery] bool? hasAired,
  546. [FromQuery] bool? isAiring,
  547. [FromQuery] DateTime? maxStartDate,
  548. [FromQuery] DateTime? minEndDate,
  549. [FromQuery] DateTime? maxEndDate,
  550. [FromQuery] bool? isMovie,
  551. [FromQuery] bool? isSeries,
  552. [FromQuery] bool? isNews,
  553. [FromQuery] bool? isKids,
  554. [FromQuery] bool? isSports,
  555. [FromQuery] int? startIndex,
  556. [FromQuery] int? limit,
  557. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
  558. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
  559. [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
  560. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
  561. [FromQuery] bool? enableImages,
  562. [FromQuery] int? imageTypeLimit,
  563. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
  564. [FromQuery] bool? enableUserData,
  565. [FromQuery] string? seriesTimerId,
  566. [FromQuery] Guid? librarySeriesId,
  567. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
  568. [FromQuery] bool enableTotalRecordCount = true)
  569. {
  570. userId = RequestHelpers.GetUserId(User, userId);
  571. var user = userId.IsNullOrEmpty()
  572. ? null
  573. : _userManager.GetUserById(userId.Value);
  574. var query = new InternalItemsQuery(user)
  575. {
  576. ChannelIds = channelIds,
  577. HasAired = hasAired,
  578. IsAiring = isAiring,
  579. EnableTotalRecordCount = enableTotalRecordCount,
  580. MinStartDate = minStartDate,
  581. MinEndDate = minEndDate,
  582. MaxStartDate = maxStartDate,
  583. MaxEndDate = maxEndDate,
  584. StartIndex = startIndex,
  585. Limit = limit,
  586. OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
  587. IsNews = isNews,
  588. IsMovie = isMovie,
  589. IsSeries = isSeries,
  590. IsKids = isKids,
  591. IsSports = isSports,
  592. SeriesTimerId = seriesTimerId,
  593. Genres = genres,
  594. GenreIds = genreIds
  595. };
  596. if (!librarySeriesId.IsNullOrEmpty())
  597. {
  598. query.IsSeries = true;
  599. var series = _libraryManager.GetItemById<Series>(librarySeriesId.Value);
  600. if (series is not null)
  601. {
  602. query.Name = series.Name;
  603. }
  604. }
  605. var dtoOptions = new DtoOptions { Fields = fields }
  606. .AddClientFields(User)
  607. .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
  608. return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
  609. }
  610. /// <summary>
  611. /// Gets available live tv epgs.
  612. /// </summary>
  613. /// <param name="body">Request body.</param>
  614. /// <response code="200">Live tv epgs returned.</response>
  615. /// <returns>
  616. /// A <see cref="Task"/> containing a <see cref="OkResult"/> which contains the live tv epgs.
  617. /// </returns>
  618. [HttpPost("Programs")]
  619. [ProducesResponseType(StatusCodes.Status200OK)]
  620. [Authorize(Policy = Policies.LiveTvAccess)]
  621. public async Task<ActionResult<QueryResult<BaseItemDto>>> GetPrograms([FromBody] GetProgramsDto body)
  622. {
  623. var user = body.UserId.IsNullOrEmpty() ? null : _userManager.GetUserById(body.UserId.Value);
  624. var query = new InternalItemsQuery(user)
  625. {
  626. ChannelIds = body.ChannelIds ?? [],
  627. HasAired = body.HasAired,
  628. IsAiring = body.IsAiring,
  629. EnableTotalRecordCount = body.EnableTotalRecordCount,
  630. MinStartDate = body.MinStartDate,
  631. MinEndDate = body.MinEndDate,
  632. MaxStartDate = body.MaxStartDate,
  633. MaxEndDate = body.MaxEndDate,
  634. StartIndex = body.StartIndex,
  635. Limit = body.Limit,
  636. OrderBy = RequestHelpers.GetOrderBy(body.SortBy ?? [], body.SortOrder ?? []),
  637. IsNews = body.IsNews,
  638. IsMovie = body.IsMovie,
  639. IsSeries = body.IsSeries,
  640. IsKids = body.IsKids,
  641. IsSports = body.IsSports,
  642. SeriesTimerId = body.SeriesTimerId,
  643. Genres = body.Genres ?? [],
  644. GenreIds = body.GenreIds ?? []
  645. };
  646. if (!body.LibrarySeriesId.IsNullOrEmpty())
  647. {
  648. query.IsSeries = true;
  649. var series = _libraryManager.GetItemById<Series>(body.LibrarySeriesId.Value);
  650. if (series is not null)
  651. {
  652. query.Name = series.Name;
  653. }
  654. }
  655. var dtoOptions = new DtoOptions { Fields = body.Fields ?? [] }
  656. .AddClientFields(User)
  657. .AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes ?? []);
  658. return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
  659. }
  660. /// <summary>
  661. /// Gets recommended live tv epgs.
  662. /// </summary>
  663. /// <param name="userId">Optional. filter by user id.</param>
  664. /// <param name="limit">Optional. The maximum number of records to return.</param>
  665. /// <param name="isAiring">Optional. Filter by programs that are currently airing, or not.</param>
  666. /// <param name="hasAired">Optional. Filter by programs that have completed airing, or not.</param>
  667. /// <param name="isSeries">Optional. Filter for series.</param>
  668. /// <param name="isMovie">Optional. Filter for movies.</param>
  669. /// <param name="isNews">Optional. Filter for news.</param>
  670. /// <param name="isKids">Optional. Filter for kids.</param>
  671. /// <param name="isSports">Optional. Filter for sports.</param>
  672. /// <param name="enableImages">Optional. Include image information in output.</param>
  673. /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
  674. /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
  675. /// <param name="genreIds">The genres to return guide information for.</param>
  676. /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
  677. /// <param name="enableUserData">Optional. include user data.</param>
  678. /// <param name="enableTotalRecordCount">Retrieve total record count.</param>
  679. /// <response code="200">Recommended epgs returned.</response>
  680. /// <returns>A <see cref="OkResult"/> containing the queryresult of recommended epgs.</returns>
  681. [HttpGet("Programs/Recommended")]
  682. [Authorize(Policy = Policies.LiveTvAccess)]
  683. [ProducesResponseType(StatusCodes.Status200OK)]
  684. public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecommendedPrograms(
  685. [FromQuery] Guid? userId,
  686. [FromQuery] int? limit,
  687. [FromQuery] bool? isAiring,
  688. [FromQuery] bool? hasAired,
  689. [FromQuery] bool? isSeries,
  690. [FromQuery] bool? isMovie,
  691. [FromQuery] bool? isNews,
  692. [FromQuery] bool? isKids,
  693. [FromQuery] bool? isSports,
  694. [FromQuery] bool? enableImages,
  695. [FromQuery] int? imageTypeLimit,
  696. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
  697. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
  698. [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
  699. [FromQuery] bool? enableUserData,
  700. [FromQuery] bool enableTotalRecordCount = true)
  701. {
  702. userId = RequestHelpers.GetUserId(User, userId);
  703. var user = userId.IsNullOrEmpty()
  704. ? null
  705. : _userManager.GetUserById(userId.Value);
  706. var query = new InternalItemsQuery(user)
  707. {
  708. IsAiring = isAiring,
  709. Limit = limit,
  710. HasAired = hasAired,
  711. IsSeries = isSeries,
  712. IsMovie = isMovie,
  713. IsKids = isKids,
  714. IsNews = isNews,
  715. IsSports = isSports,
  716. EnableTotalRecordCount = enableTotalRecordCount,
  717. GenreIds = genreIds
  718. };
  719. var dtoOptions = new DtoOptions { Fields = fields }
  720. .AddClientFields(User)
  721. .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
  722. return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
  723. }
  724. /// <summary>
  725. /// Gets a live tv program.
  726. /// </summary>
  727. /// <param name="programId">Program id.</param>
  728. /// <param name="userId">Optional. Attach user data.</param>
  729. /// <response code="200">Program returned.</response>
  730. /// <returns>An <see cref="OkResult"/> containing the livetv program.</returns>
  731. [HttpGet("Programs/{programId}")]
  732. [Authorize(Policy = Policies.LiveTvAccess)]
  733. [ProducesResponseType(StatusCodes.Status200OK)]
  734. public async Task<ActionResult<BaseItemDto>> GetProgram(
  735. [FromRoute, Required] string programId,
  736. [FromQuery] Guid? userId)
  737. {
  738. userId = RequestHelpers.GetUserId(User, userId);
  739. var user = userId.IsNullOrEmpty()
  740. ? null
  741. : _userManager.GetUserById(userId.Value);
  742. return await _liveTvManager.GetProgram(programId, CancellationToken.None, user).ConfigureAwait(false);
  743. }
  744. /// <summary>
  745. /// Deletes a live tv recording.
  746. /// </summary>
  747. /// <param name="recordingId">Recording id.</param>
  748. /// <response code="204">Recording deleted.</response>
  749. /// <response code="404">Item not found.</response>
  750. /// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
  751. [HttpDelete("Recordings/{recordingId}")]
  752. [Authorize(Policy = Policies.LiveTvManagement)]
  753. [ProducesResponseType(StatusCodes.Status204NoContent)]
  754. [ProducesResponseType(StatusCodes.Status404NotFound)]
  755. public ActionResult DeleteRecording([FromRoute, Required] Guid recordingId)
  756. {
  757. var item = _libraryManager.GetItemById<BaseItem>(recordingId, User.GetUserId());
  758. if (item is null)
  759. {
  760. return NotFound();
  761. }
  762. _libraryManager.DeleteItem(item, new DeleteOptions
  763. {
  764. DeleteFileLocation = false
  765. });
  766. return NoContent();
  767. }
  768. /// <summary>
  769. /// Cancels a live tv timer.
  770. /// </summary>
  771. /// <param name="timerId">Timer id.</param>
  772. /// <response code="204">Timer deleted.</response>
  773. /// <returns>A <see cref="NoContentResult"/>.</returns>
  774. [HttpDelete("Timers/{timerId}")]
  775. [Authorize(Policy = Policies.LiveTvManagement)]
  776. [ProducesResponseType(StatusCodes.Status204NoContent)]
  777. public async Task<ActionResult> CancelTimer([FromRoute, Required] string timerId)
  778. {
  779. await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false);
  780. return NoContent();
  781. }
  782. /// <summary>
  783. /// Updates a live tv timer.
  784. /// </summary>
  785. /// <param name="timerId">Timer id.</param>
  786. /// <param name="timerInfo">New timer info.</param>
  787. /// <response code="204">Timer updated.</response>
  788. /// <returns>A <see cref="NoContentResult"/>.</returns>
  789. [HttpPost("Timers/{timerId}")]
  790. [Authorize(Policy = Policies.LiveTvManagement)]
  791. [ProducesResponseType(StatusCodes.Status204NoContent)]
  792. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")]
  793. public async Task<ActionResult> UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo)
  794. {
  795. await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false);
  796. return NoContent();
  797. }
  798. /// <summary>
  799. /// Creates a live tv timer.
  800. /// </summary>
  801. /// <param name="timerInfo">New timer info.</param>
  802. /// <response code="204">Timer created.</response>
  803. /// <returns>A <see cref="NoContentResult"/>.</returns>
  804. [HttpPost("Timers")]
  805. [Authorize(Policy = Policies.LiveTvManagement)]
  806. [ProducesResponseType(StatusCodes.Status204NoContent)]
  807. public async Task<ActionResult> CreateTimer([FromBody] TimerInfoDto timerInfo)
  808. {
  809. await _liveTvManager.CreateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false);
  810. return NoContent();
  811. }
  812. /// <summary>
  813. /// Gets a live tv series timer.
  814. /// </summary>
  815. /// <param name="timerId">Timer id.</param>
  816. /// <response code="200">Series timer returned.</response>
  817. /// <response code="404">Series timer not found.</response>
  818. /// <returns>A <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if timer not found.</returns>
  819. [HttpGet("SeriesTimers/{timerId}")]
  820. [Authorize(Policy = Policies.LiveTvAccess)]
  821. [ProducesResponseType(StatusCodes.Status200OK)]
  822. [ProducesResponseType(StatusCodes.Status404NotFound)]
  823. public async Task<ActionResult<SeriesTimerInfoDto>> GetSeriesTimer([FromRoute, Required] string timerId)
  824. {
  825. var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false);
  826. if (timer is null)
  827. {
  828. return NotFound();
  829. }
  830. return timer;
  831. }
  832. /// <summary>
  833. /// Gets live tv series timers.
  834. /// </summary>
  835. /// <param name="sortBy">Optional. Sort by SortName or Priority.</param>
  836. /// <param name="sortOrder">Optional. Sort in Ascending or Descending order.</param>
  837. /// <response code="200">Timers returned.</response>
  838. /// <returns>An <see cref="OkResult"/> of live tv series timers.</returns>
  839. [HttpGet("SeriesTimers")]
  840. [Authorize(Policy = Policies.LiveTvAccess)]
  841. [ProducesResponseType(StatusCodes.Status200OK)]
  842. public async Task<ActionResult<QueryResult<SeriesTimerInfoDto>>> GetSeriesTimers([FromQuery] string? sortBy, [FromQuery] SortOrder? sortOrder)
  843. {
  844. return await _liveTvManager.GetSeriesTimers(
  845. new SeriesTimerQuery
  846. {
  847. SortOrder = sortOrder ?? SortOrder.Ascending,
  848. SortBy = sortBy
  849. },
  850. CancellationToken.None).ConfigureAwait(false);
  851. }
  852. /// <summary>
  853. /// Cancels a live tv series timer.
  854. /// </summary>
  855. /// <param name="timerId">Timer id.</param>
  856. /// <response code="204">Timer cancelled.</response>
  857. /// <returns>A <see cref="NoContentResult"/>.</returns>
  858. [HttpDelete("SeriesTimers/{timerId}")]
  859. [Authorize(Policy = Policies.LiveTvManagement)]
  860. [ProducesResponseType(StatusCodes.Status204NoContent)]
  861. public async Task<ActionResult> CancelSeriesTimer([FromRoute, Required] string timerId)
  862. {
  863. await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false);
  864. return NoContent();
  865. }
  866. /// <summary>
  867. /// Updates a live tv series timer.
  868. /// </summary>
  869. /// <param name="timerId">Timer id.</param>
  870. /// <param name="seriesTimerInfo">New series timer info.</param>
  871. /// <response code="204">Series timer updated.</response>
  872. /// <returns>A <see cref="NoContentResult"/>.</returns>
  873. [HttpPost("SeriesTimers/{timerId}")]
  874. [Authorize(Policy = Policies.LiveTvManagement)]
  875. [ProducesResponseType(StatusCodes.Status204NoContent)]
  876. [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")]
  877. public async Task<ActionResult> UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo)
  878. {
  879. await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false);
  880. return NoContent();
  881. }
  882. /// <summary>
  883. /// Creates a live tv series timer.
  884. /// </summary>
  885. /// <param name="seriesTimerInfo">New series timer info.</param>
  886. /// <response code="204">Series timer info created.</response>
  887. /// <returns>A <see cref="NoContentResult"/>.</returns>
  888. [HttpPost("SeriesTimers")]
  889. [Authorize(Policy = Policies.LiveTvManagement)]
  890. [ProducesResponseType(StatusCodes.Status204NoContent)]
  891. public async Task<ActionResult> CreateSeriesTimer([FromBody] SeriesTimerInfoDto seriesTimerInfo)
  892. {
  893. await _liveTvManager.CreateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false);
  894. return NoContent();
  895. }
  896. /// <summary>
  897. /// Get recording group.
  898. /// </summary>
  899. /// <param name="groupId">Group id.</param>
  900. /// <returns>A <see cref="NotFoundResult"/>.</returns>
  901. [HttpGet("Recordings/Groups/{groupId}")]
  902. [Authorize(Policy = Policies.LiveTvAccess)]
  903. [ProducesResponseType(StatusCodes.Status404NotFound)]
  904. [Obsolete("This endpoint is obsolete.")]
  905. public ActionResult<BaseItemDto> GetRecordingGroup([FromRoute, Required] Guid groupId)
  906. {
  907. return NotFound();
  908. }
  909. /// <summary>
  910. /// Get guid info.
  911. /// </summary>
  912. /// <response code="200">Guid info returned.</response>
  913. /// <returns>An <see cref="OkResult"/> containing the guide info.</returns>
  914. [HttpGet("GuideInfo")]
  915. [Authorize(Policy = Policies.LiveTvAccess)]
  916. [ProducesResponseType(StatusCodes.Status200OK)]
  917. public ActionResult<GuideInfo> GetGuideInfo()
  918. => _guideManager.GetGuideInfo();
  919. /// <summary>
  920. /// Adds a tuner host.
  921. /// </summary>
  922. /// <param name="tunerHostInfo">New tuner host.</param>
  923. /// <response code="200">Created tuner host returned.</response>
  924. /// <returns>A <see cref="OkResult"/> containing the created tuner host.</returns>
  925. [HttpPost("TunerHosts")]
  926. [Authorize(Policy = Policies.LiveTvManagement)]
  927. [ProducesResponseType(StatusCodes.Status200OK)]
  928. public async Task<ActionResult<TunerHostInfo>> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo)
  929. => await _tunerHostManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
  930. /// <summary>
  931. /// Deletes a tuner host.
  932. /// </summary>
  933. /// <param name="id">Tuner host id.</param>
  934. /// <response code="204">Tuner host deleted.</response>
  935. /// <returns>A <see cref="NoContentResult"/>.</returns>
  936. [HttpDelete("TunerHosts")]
  937. [Authorize(Policy = Policies.LiveTvManagement)]
  938. [ProducesResponseType(StatusCodes.Status204NoContent)]
  939. public ActionResult DeleteTunerHost([FromQuery] string? id)
  940. {
  941. var config = _configurationManager.GetConfiguration<LiveTvOptions>("livetv");
  942. config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
  943. _configurationManager.SaveConfiguration("livetv", config);
  944. return NoContent();
  945. }
  946. /// <summary>
  947. /// Gets default listings provider info.
  948. /// </summary>
  949. /// <response code="200">Default listings provider info returned.</response>
  950. /// <returns>An <see cref="OkResult"/> containing the default listings provider info.</returns>
  951. [HttpGet("ListingProviders/Default")]
  952. [Authorize(Policy = Policies.LiveTvAccess)]
  953. [ProducesResponseType(StatusCodes.Status200OK)]
  954. public ActionResult<ListingsProviderInfo> GetDefaultListingProvider()
  955. {
  956. return new ListingsProviderInfo();
  957. }
  958. /// <summary>
  959. /// Adds a listings provider.
  960. /// </summary>
  961. /// <param name="pw">Password.</param>
  962. /// <param name="listingsProviderInfo">New listings info.</param>
  963. /// <param name="validateListings">Validate listings.</param>
  964. /// <param name="validateLogin">Validate login.</param>
  965. /// <response code="200">Created listings provider returned.</response>
  966. /// <returns>A <see cref="OkResult"/> containing the created listings provider.</returns>
  967. [HttpPost("ListingProviders")]
  968. [Authorize(Policy = Policies.LiveTvManagement)]
  969. [ProducesResponseType(StatusCodes.Status200OK)]
  970. [SuppressMessage("Microsoft.Performance", "CA5350:RemoveSha1", MessageId = "AddListingProvider", Justification = "Imported from ServiceStack")]
  971. public async Task<ActionResult<ListingsProviderInfo>> AddListingProvider(
  972. [FromQuery] string? pw,
  973. [FromBody] ListingsProviderInfo listingsProviderInfo,
  974. [FromQuery] bool validateListings = false,
  975. [FromQuery] bool validateLogin = false)
  976. {
  977. if (!string.IsNullOrEmpty(pw))
  978. {
  979. // TODO: remove ToLower when Convert.ToHexString supports lowercase
  980. // Schedules Direct requires the hex to be lowercase
  981. listingsProviderInfo.Password = Convert.ToHexString(SHA1.HashData(Encoding.UTF8.GetBytes(pw))).ToLowerInvariant();
  982. }
  983. return await _listingsManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false);
  984. }
  985. /// <summary>
  986. /// Delete listing provider.
  987. /// </summary>
  988. /// <param name="id">Listing provider id.</param>
  989. /// <response code="204">Listing provider deleted.</response>
  990. /// <returns>A <see cref="NoContentResult"/>.</returns>
  991. [HttpDelete("ListingProviders")]
  992. [Authorize(Policy = Policies.LiveTvManagement)]
  993. [ProducesResponseType(StatusCodes.Status204NoContent)]
  994. public ActionResult DeleteListingProvider([FromQuery] string? id)
  995. {
  996. _listingsManager.DeleteListingsProvider(id);
  997. return NoContent();
  998. }
  999. /// <summary>
  1000. /// Gets available lineups.
  1001. /// </summary>
  1002. /// <param name="id">Provider id.</param>
  1003. /// <param name="type">Provider type.</param>
  1004. /// <param name="location">Location.</param>
  1005. /// <param name="country">Country.</param>
  1006. /// <response code="200">Available lineups returned.</response>
  1007. /// <returns>A <see cref="OkResult"/> containing the available lineups.</returns>
  1008. [HttpGet("ListingProviders/Lineups")]
  1009. [Authorize(Policy = Policies.LiveTvAccess)]
  1010. [ProducesResponseType(StatusCodes.Status200OK)]
  1011. public async Task<ActionResult<IEnumerable<NameIdPair>>> GetLineups(
  1012. [FromQuery] string? id,
  1013. [FromQuery] string? type,
  1014. [FromQuery] string? location,
  1015. [FromQuery] string? country)
  1016. => await _listingsManager.GetLineups(type, id, country, location).ConfigureAwait(false);
  1017. /// <summary>
  1018. /// Gets available countries.
  1019. /// </summary>
  1020. /// <response code="200">Available countries returned.</response>
  1021. /// <returns>A <see cref="FileResult"/> containing the available countries.</returns>
  1022. [HttpGet("ListingProviders/SchedulesDirect/Countries")]
  1023. [Authorize(Policy = Policies.LiveTvAccess)]
  1024. [ProducesResponseType(StatusCodes.Status200OK)]
  1025. [ProducesFile(MediaTypeNames.Application.Json)]
  1026. public async Task<ActionResult> GetSchedulesDirectCountries()
  1027. {
  1028. var client = _httpClientFactory.CreateClient(NamedClient.Default);
  1029. // https://json.schedulesdirect.org/20141201/available/countries
  1030. // Can't dispose the response as it's required up the call chain.
  1031. var response = await client.GetAsync(new Uri("https://json.schedulesdirect.org/20141201/available/countries"))
  1032. .ConfigureAwait(false);
  1033. return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json);
  1034. }
  1035. /// <summary>
  1036. /// Get channel mapping options.
  1037. /// </summary>
  1038. /// <param name="providerId">Provider id.</param>
  1039. /// <response code="200">Channel mapping options returned.</response>
  1040. /// <returns>An <see cref="OkResult"/> containing the channel mapping options.</returns>
  1041. [HttpGet("ChannelMappingOptions")]
  1042. [Authorize(Policy = Policies.LiveTvAccess)]
  1043. [ProducesResponseType(StatusCodes.Status200OK)]
  1044. public Task<ChannelMappingOptionsDto> GetChannelMappingOptions([FromQuery] string? providerId)
  1045. => _listingsManager.GetChannelMappingOptions(providerId);
  1046. /// <summary>
  1047. /// Set channel mappings.
  1048. /// </summary>
  1049. /// <param name="dto">The set channel mapping dto.</param>
  1050. /// <response code="200">Created channel mapping returned.</response>
  1051. /// <returns>An <see cref="OkResult"/> containing the created channel mapping.</returns>
  1052. [HttpPost("ChannelMappings")]
  1053. [Authorize(Policy = Policies.LiveTvManagement)]
  1054. [ProducesResponseType(StatusCodes.Status200OK)]
  1055. public Task<TunerChannelMapping> SetChannelMapping([FromBody, Required] SetChannelMappingDto dto)
  1056. => _listingsManager.SetChannelMapping(dto.ProviderId, dto.TunerChannelId, dto.ProviderChannelId);
  1057. /// <summary>
  1058. /// Get tuner host types.
  1059. /// </summary>
  1060. /// <response code="200">Tuner host types returned.</response>
  1061. /// <returns>An <see cref="OkResult"/> containing the tuner host types.</returns>
  1062. [HttpGet("TunerHosts/Types")]
  1063. [Authorize(Policy = Policies.LiveTvAccess)]
  1064. [ProducesResponseType(StatusCodes.Status200OK)]
  1065. public IEnumerable<NameIdPair> GetTunerHostTypes()
  1066. => _tunerHostManager.GetTunerHostTypes();
  1067. /// <summary>
  1068. /// Discover tuners.
  1069. /// </summary>
  1070. /// <param name="newDevicesOnly">Only discover new tuners.</param>
  1071. /// <response code="200">Tuners returned.</response>
  1072. /// <returns>An <see cref="OkResult"/> containing the tuners.</returns>
  1073. [HttpGet("Tuners/Discvover", Name = "DiscvoverTuners")]
  1074. [HttpGet("Tuners/Discover")]
  1075. [Authorize(Policy = Policies.LiveTvManagement)]
  1076. [ProducesResponseType(StatusCodes.Status200OK)]
  1077. public IAsyncEnumerable<TunerHostInfo> DiscoverTuners([FromQuery] bool newDevicesOnly = false)
  1078. => _tunerHostManager.DiscoverTuners(newDevicesOnly);
  1079. /// <summary>
  1080. /// Gets a live tv recording stream.
  1081. /// </summary>
  1082. /// <param name="recordingId">Recording id.</param>
  1083. /// <response code="200">Recording stream returned.</response>
  1084. /// <response code="404">Recording not found.</response>
  1085. /// <returns>
  1086. /// An <see cref="OkResult"/> containing the recording stream on success,
  1087. /// or a <see cref="NotFoundResult"/> if recording not found.
  1088. /// </returns>
  1089. [HttpGet("LiveRecordings/{recordingId}/stream")]
  1090. [ProducesResponseType(StatusCodes.Status200OK)]
  1091. [ProducesResponseType(StatusCodes.Status404NotFound)]
  1092. [ProducesVideoFile]
  1093. public ActionResult GetLiveRecordingFile([FromRoute, Required] string recordingId)
  1094. {
  1095. var path = _recordingsManager.GetActiveRecordingPath(recordingId);
  1096. if (string.IsNullOrWhiteSpace(path))
  1097. {
  1098. return NotFound();
  1099. }
  1100. var stream = new ProgressiveFileStream(path, null, _transcodeManager);
  1101. return new FileStreamResult(stream, MimeTypes.GetMimeType(path));
  1102. }
  1103. /// <summary>
  1104. /// Gets a live tv channel stream.
  1105. /// </summary>
  1106. /// <param name="streamId">Stream id.</param>
  1107. /// <param name="container">Container type.</param>
  1108. /// <response code="200">Stream returned.</response>
  1109. /// <response code="404">Stream not found.</response>
  1110. /// <returns>
  1111. /// An <see cref="OkResult"/> containing the channel stream on success,
  1112. /// or a <see cref="NotFoundResult"/> if stream not found.
  1113. /// </returns>
  1114. [HttpGet("LiveStreamFiles/{streamId}/stream.{container}")]
  1115. [ProducesResponseType(StatusCodes.Status200OK)]
  1116. [ProducesResponseType(StatusCodes.Status404NotFound)]
  1117. [ProducesVideoFile]
  1118. public ActionResult GetLiveStreamFile([FromRoute, Required] string streamId, [FromRoute, Required] string container)
  1119. {
  1120. var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfoByUniqueId(streamId);
  1121. if (liveStreamInfo is null)
  1122. {
  1123. return NotFound();
  1124. }
  1125. var liveStream = new ProgressiveFileStream(liveStreamInfo.GetStream());
  1126. return new FileStreamResult(liveStream, MimeTypes.GetMimeType("file." + container));
  1127. }
  1128. }