LiveTvService.cs 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Security.Cryptography;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using Jellyfin.Data.Enums;
  11. using MediaBrowser.Api.UserLibrary;
  12. using MediaBrowser.Common;
  13. using MediaBrowser.Common.Configuration;
  14. using MediaBrowser.Common.Net;
  15. using MediaBrowser.Controller.Configuration;
  16. using MediaBrowser.Controller.Dto;
  17. using MediaBrowser.Controller.Entities;
  18. using MediaBrowser.Controller.Entities.TV;
  19. using MediaBrowser.Controller.Library;
  20. using MediaBrowser.Controller.LiveTv;
  21. using MediaBrowser.Controller.Net;
  22. using MediaBrowser.Model.Dto;
  23. using MediaBrowser.Model.Entities;
  24. using MediaBrowser.Model.IO;
  25. using MediaBrowser.Model.LiveTv;
  26. using MediaBrowser.Model.Querying;
  27. using MediaBrowser.Model.Services;
  28. using Microsoft.Extensions.Logging;
  29. using Microsoft.Net.Http.Headers;
  30. namespace MediaBrowser.Api.LiveTv
  31. {
  32. /// <summary>
  33. /// This is insecure right now to avoid windows phone refactoring
  34. /// </summary>
  35. [Route("/LiveTv/Info", "GET", Summary = "Gets available live tv services.")]
  36. [Authenticated]
  37. public class GetLiveTvInfo : IReturn<LiveTvInfo>
  38. {
  39. }
  40. [Route("/LiveTv/Channels", "GET", Summary = "Gets available live tv channels.")]
  41. [Authenticated]
  42. public class GetChannels : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
  43. {
  44. [ApiMember(Name = "Type", Description = "Optional filter by channel type.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  45. public ChannelType? Type { get; set; }
  46. [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  47. public Guid UserId { get; set; }
  48. /// <summary>
  49. /// Skips over a given number of items within the results. Use for paging.
  50. /// </summary>
  51. /// <value>The start index.</value>
  52. [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  53. public int? StartIndex { get; set; }
  54. [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  55. public bool? IsMovie { get; set; }
  56. [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  57. public bool? IsSeries { get; set; }
  58. [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  59. public bool? IsNews { get; set; }
  60. [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  61. public bool? IsKids { get; set; }
  62. [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  63. public bool? IsSports { get; set; }
  64. /// <summary>
  65. /// The maximum number of items to return
  66. /// </summary>
  67. /// <value>The limit.</value>
  68. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  69. public int? Limit { get; set; }
  70. [ApiMember(Name = "IsFavorite", Description = "Filter by channels that are favorites, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  71. public bool? IsFavorite { get; set; }
  72. [ApiMember(Name = "IsLiked", Description = "Filter by channels that are liked, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  73. public bool? IsLiked { get; set; }
  74. [ApiMember(Name = "IsDisliked", Description = "Filter by channels that are disliked, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  75. public bool? IsDisliked { get; set; }
  76. [ApiMember(Name = "EnableFavoriteSorting", Description = "Incorporate favorite and like status into channel sorting.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  77. public bool EnableFavoriteSorting { get; set; }
  78. [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  79. public bool? EnableImages { get; set; }
  80. [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  81. public int? ImageTypeLimit { get; set; }
  82. [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  83. public string EnableImageTypes { get; set; }
  84. /// <summary>
  85. /// Fields to return within the items, in addition to basic information
  86. /// </summary>
  87. /// <value>The fields.</value>
  88. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
  89. public string Fields { get; set; }
  90. [ApiMember(Name = "AddCurrentProgram", Description = "Optional. Adds current program info to each channel", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  91. public bool AddCurrentProgram { get; set; }
  92. [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  93. public bool? EnableUserData { get; set; }
  94. public string SortBy { get; set; }
  95. public SortOrder? SortOrder { get; set; }
  96. /// <summary>
  97. /// Gets the order by.
  98. /// </summary>
  99. /// <returns>IEnumerable{ItemSortBy}.</returns>
  100. public string[] GetOrderBy()
  101. {
  102. var val = SortBy;
  103. if (string.IsNullOrEmpty(val))
  104. {
  105. return Array.Empty<string>();
  106. }
  107. return val.Split(',');
  108. }
  109. public GetChannels()
  110. {
  111. AddCurrentProgram = true;
  112. }
  113. }
  114. [Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")]
  115. [Authenticated]
  116. public class GetChannel : IReturn<BaseItemDto>
  117. {
  118. /// <summary>
  119. /// Gets or sets the id.
  120. /// </summary>
  121. /// <value>The id.</value>
  122. [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  123. public string Id { get; set; }
  124. [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  125. public Guid UserId { get; set; }
  126. }
  127. [Route("/LiveTv/Recordings", "GET", Summary = "Gets live tv recordings")]
  128. [Authenticated]
  129. public class GetRecordings : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
  130. {
  131. [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  132. public string ChannelId { get; set; }
  133. [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  134. public Guid UserId { get; set; }
  135. [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  136. public int? StartIndex { get; set; }
  137. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  138. public int? Limit { get; set; }
  139. [ApiMember(Name = "Status", Description = "Optional filter by recording status.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  140. public RecordingStatus? Status { get; set; }
  141. [ApiMember(Name = "Status", Description = "Optional filter by recordings that are in progress, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  142. public bool? IsInProgress { get; set; }
  143. [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  144. public string SeriesTimerId { get; set; }
  145. [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  146. public bool? EnableImages { get; set; }
  147. [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  148. public int? ImageTypeLimit { get; set; }
  149. [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  150. public string EnableImageTypes { get; set; }
  151. /// <summary>
  152. /// Fields to return within the items, in addition to basic information
  153. /// </summary>
  154. /// <value>The fields.</value>
  155. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
  156. public string Fields { get; set; }
  157. public bool EnableTotalRecordCount { get; set; }
  158. [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  159. public bool? EnableUserData { get; set; }
  160. public bool? IsMovie { get; set; }
  161. public bool? IsSeries { get; set; }
  162. public bool? IsKids { get; set; }
  163. public bool? IsSports { get; set; }
  164. public bool? IsNews { get; set; }
  165. public bool? IsLibraryItem { get; set; }
  166. public GetRecordings()
  167. {
  168. EnableTotalRecordCount = true;
  169. }
  170. }
  171. [Route("/LiveTv/Recordings/Series", "GET", Summary = "Gets live tv recordings")]
  172. [Authenticated]
  173. public class GetRecordingSeries : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
  174. {
  175. [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  176. public string ChannelId { get; set; }
  177. [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  178. public string UserId { get; set; }
  179. [ApiMember(Name = "GroupId", Description = "Optional filter by recording group.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  180. public string GroupId { get; set; }
  181. [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  182. public int? StartIndex { get; set; }
  183. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  184. public int? Limit { get; set; }
  185. [ApiMember(Name = "Status", Description = "Optional filter by recording status.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  186. public RecordingStatus? Status { get; set; }
  187. [ApiMember(Name = "Status", Description = "Optional filter by recordings that are in progress, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  188. public bool? IsInProgress { get; set; }
  189. [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  190. public string SeriesTimerId { get; set; }
  191. [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  192. public bool? EnableImages { get; set; }
  193. [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  194. public int? ImageTypeLimit { get; set; }
  195. [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  196. public string EnableImageTypes { get; set; }
  197. /// <summary>
  198. /// Fields to return within the items, in addition to basic information
  199. /// </summary>
  200. /// <value>The fields.</value>
  201. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
  202. public string Fields { get; set; }
  203. public bool EnableTotalRecordCount { get; set; }
  204. [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  205. public bool? EnableUserData { get; set; }
  206. public GetRecordingSeries()
  207. {
  208. EnableTotalRecordCount = true;
  209. }
  210. }
  211. [Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")]
  212. [Authenticated]
  213. public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>>
  214. {
  215. [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  216. public string UserId { get; set; }
  217. }
  218. [Route("/LiveTv/Recordings/Folders", "GET", Summary = "Gets recording folders")]
  219. [Authenticated]
  220. public class GetRecordingFolders : IReturn<BaseItemDto[]>
  221. {
  222. [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  223. public Guid UserId { get; set; }
  224. }
  225. [Route("/LiveTv/Recordings/{Id}", "GET", Summary = "Gets a live tv recording")]
  226. [Authenticated]
  227. public class GetRecording : IReturn<BaseItemDto>
  228. {
  229. [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  230. public string Id { get; set; }
  231. [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  232. public Guid UserId { get; set; }
  233. }
  234. [Route("/LiveTv/Tuners/{Id}/Reset", "POST", Summary = "Resets a tv tuner")]
  235. [Authenticated]
  236. public class ResetTuner : IReturnVoid
  237. {
  238. [ApiMember(Name = "Id", Description = "Tuner Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  239. public string Id { get; set; }
  240. }
  241. [Route("/LiveTv/Timers/{Id}", "GET", Summary = "Gets a live tv timer")]
  242. [Authenticated]
  243. public class GetTimer : IReturn<TimerInfoDto>
  244. {
  245. [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  246. public string Id { get; set; }
  247. }
  248. [Route("/LiveTv/Timers/Defaults", "GET", Summary = "Gets default values for a new timer")]
  249. [Authenticated]
  250. public class GetDefaultTimer : IReturn<SeriesTimerInfoDto>
  251. {
  252. [ApiMember(Name = "ProgramId", Description = "Optional, to attach default values based on a program.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  253. public string ProgramId { get; set; }
  254. }
  255. [Route("/LiveTv/Timers", "GET", Summary = "Gets live tv timers")]
  256. [Authenticated]
  257. public class GetTimers : IReturn<QueryResult<TimerInfoDto>>
  258. {
  259. [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  260. public string ChannelId { get; set; }
  261. [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by timers belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  262. public string SeriesTimerId { get; set; }
  263. public bool? IsActive { get; set; }
  264. public bool? IsScheduled { get; set; }
  265. }
  266. [Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
  267. [Authenticated]
  268. public class GetPrograms : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
  269. {
  270. [ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  271. public string ChannelIds { get; set; }
  272. [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  273. public Guid UserId { get; set; }
  274. [ApiMember(Name = "MinStartDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  275. public string MinStartDate { get; set; }
  276. [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  277. public bool? HasAired { get; set; }
  278. public bool? IsAiring { get; set; }
  279. [ApiMember(Name = "MaxStartDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  280. public string MaxStartDate { get; set; }
  281. [ApiMember(Name = "MinEndDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  282. public string MinEndDate { get; set; }
  283. [ApiMember(Name = "MaxEndDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  284. public string MaxEndDate { get; set; }
  285. [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  286. public bool? IsMovie { get; set; }
  287. [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  288. public bool? IsSeries { get; set; }
  289. [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  290. public bool? IsNews { get; set; }
  291. [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  292. public bool? IsKids { get; set; }
  293. [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  294. public bool? IsSports { get; set; }
  295. [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  296. public int? StartIndex { get; set; }
  297. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  298. public int? Limit { get; set; }
  299. [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Name, StartDate", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
  300. public string SortBy { get; set; }
  301. [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  302. public string SortOrder { get; set; }
  303. [ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  304. public string Genres { get; set; }
  305. [ApiMember(Name = "GenreIds", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  306. public string GenreIds { get; set; }
  307. [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  308. public bool? EnableImages { get; set; }
  309. public bool EnableTotalRecordCount { get; set; }
  310. [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  311. public int? ImageTypeLimit { get; set; }
  312. [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  313. public string EnableImageTypes { get; set; }
  314. [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  315. public bool? EnableUserData { get; set; }
  316. public string SeriesTimerId { get; set; }
  317. public Guid LibrarySeriesId { get; set; }
  318. /// <summary>
  319. /// Fields to return within the items, in addition to basic information
  320. /// </summary>
  321. /// <value>The fields.</value>
  322. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
  323. public string Fields { get; set; }
  324. public GetPrograms()
  325. {
  326. EnableTotalRecordCount = true;
  327. }
  328. }
  329. [Route("/LiveTv/Programs/Recommended", "GET", Summary = "Gets available live tv epgs..")]
  330. [Authenticated]
  331. public class GetRecommendedPrograms : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
  332. {
  333. public bool EnableTotalRecordCount { get; set; }
  334. public GetRecommendedPrograms()
  335. {
  336. EnableTotalRecordCount = true;
  337. }
  338. [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  339. public Guid UserId { get; set; }
  340. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  341. public int? Limit { get; set; }
  342. [ApiMember(Name = "IsAiring", Description = "Optional. Filter by programs that are currently airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  343. public bool? IsAiring { get; set; }
  344. [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
  345. public bool? HasAired { get; set; }
  346. [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  347. public bool? IsSeries { get; set; }
  348. [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  349. public bool? IsMovie { get; set; }
  350. [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  351. public bool? IsNews { get; set; }
  352. [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  353. public bool? IsKids { get; set; }
  354. [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
  355. public bool? IsSports { get; set; }
  356. [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  357. public bool? EnableImages { get; set; }
  358. [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
  359. public int? ImageTypeLimit { get; set; }
  360. [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  361. public string EnableImageTypes { get; set; }
  362. [ApiMember(Name = "GenreIds", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  363. public string GenreIds { get; set; }
  364. /// <summary>
  365. /// Fields to return within the items, in addition to basic information
  366. /// </summary>
  367. /// <value>The fields.</value>
  368. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
  369. public string Fields { get; set; }
  370. [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
  371. public bool? EnableUserData { get; set; }
  372. }
  373. [Route("/LiveTv/Programs/{Id}", "GET", Summary = "Gets a live tv program")]
  374. [Authenticated]
  375. public class GetProgram : IReturn<BaseItemDto>
  376. {
  377. [ApiMember(Name = "Id", Description = "Program Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  378. public string Id { get; set; }
  379. [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  380. public Guid UserId { get; set; }
  381. }
  382. [Route("/LiveTv/Recordings/{Id}", "DELETE", Summary = "Deletes a live tv recording")]
  383. [Authenticated]
  384. public class DeleteRecording : IReturnVoid
  385. {
  386. [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
  387. public Guid Id { get; set; }
  388. }
  389. [Route("/LiveTv/Timers/{Id}", "DELETE", Summary = "Cancels a live tv timer")]
  390. [Authenticated]
  391. public class CancelTimer : IReturnVoid
  392. {
  393. [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
  394. public string Id { get; set; }
  395. }
  396. [Route("/LiveTv/Timers/{Id}", "POST", Summary = "Updates a live tv timer")]
  397. [Authenticated]
  398. public class UpdateTimer : TimerInfoDto, IReturnVoid
  399. {
  400. }
  401. [Route("/LiveTv/Timers", "POST", Summary = "Creates a live tv timer")]
  402. [Authenticated]
  403. public class CreateTimer : TimerInfoDto, IReturnVoid
  404. {
  405. }
  406. [Route("/LiveTv/SeriesTimers/{Id}", "GET", Summary = "Gets a live tv series timer")]
  407. [Authenticated]
  408. public class GetSeriesTimer : IReturn<TimerInfoDto>
  409. {
  410. [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  411. public string Id { get; set; }
  412. }
  413. [Route("/LiveTv/SeriesTimers", "GET", Summary = "Gets live tv series timers")]
  414. [Authenticated]
  415. public class GetSeriesTimers : IReturn<QueryResult<SeriesTimerInfoDto>>
  416. {
  417. [ApiMember(Name = "SortBy", Description = "Optional. Sort by SortName or Priority", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  418. public string SortBy { get; set; }
  419. [ApiMember(Name = "SortOrder", Description = "Optional. Sort in Ascending or Descending order", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
  420. public SortOrder SortOrder { get; set; }
  421. }
  422. [Route("/LiveTv/SeriesTimers/{Id}", "DELETE", Summary = "Cancels a live tv series timer")]
  423. [Authenticated]
  424. public class CancelSeriesTimer : IReturnVoid
  425. {
  426. [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
  427. public string Id { get; set; }
  428. }
  429. [Route("/LiveTv/SeriesTimers/{Id}", "POST", Summary = "Updates a live tv series timer")]
  430. [Authenticated]
  431. public class UpdateSeriesTimer : SeriesTimerInfoDto, IReturnVoid
  432. {
  433. }
  434. [Route("/LiveTv/SeriesTimers", "POST", Summary = "Creates a live tv series timer")]
  435. [Authenticated]
  436. public class CreateSeriesTimer : SeriesTimerInfoDto, IReturnVoid
  437. {
  438. }
  439. [Route("/LiveTv/Recordings/Groups/{Id}", "GET", Summary = "Gets a recording group")]
  440. [Authenticated]
  441. public class GetRecordingGroup : IReturn<BaseItemDto>
  442. {
  443. [ApiMember(Name = "Id", Description = "Recording group Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  444. public string Id { get; set; }
  445. }
  446. [Route("/LiveTv/GuideInfo", "GET", Summary = "Gets guide info")]
  447. [Authenticated]
  448. public class GetGuideInfo : IReturn<GuideInfo>
  449. {
  450. }
  451. [Route("/LiveTv/TunerHosts", "POST", Summary = "Adds a tuner host")]
  452. [Authenticated]
  453. public class AddTunerHost : TunerHostInfo, IReturn<TunerHostInfo>
  454. {
  455. }
  456. [Route("/LiveTv/TunerHosts", "DELETE", Summary = "Deletes a tuner host")]
  457. [Authenticated]
  458. public class DeleteTunerHost : IReturnVoid
  459. {
  460. [ApiMember(Name = "Id", Description = "Tuner host id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
  461. public string Id { get; set; }
  462. }
  463. [Route("/LiveTv/ListingProviders/Default", "GET")]
  464. [Authenticated]
  465. public class GetDefaultListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
  466. {
  467. }
  468. [Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")]
  469. [Authenticated]
  470. public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
  471. {
  472. public bool ValidateLogin { get; set; }
  473. public bool ValidateListings { get; set; }
  474. public string Pw { get; set; }
  475. }
  476. [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
  477. [Authenticated]
  478. public class DeleteListingProvider : IReturnVoid
  479. {
  480. [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
  481. public string Id { get; set; }
  482. }
  483. [Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")]
  484. [Authenticated]
  485. public class GetLineups : IReturn<List<NameIdPair>>
  486. {
  487. [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  488. public string Id { get; set; }
  489. [ApiMember(Name = "Type", Description = "Provider Type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  490. public string Type { get; set; }
  491. [ApiMember(Name = "Location", Description = "Location", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  492. public string Location { get; set; }
  493. [ApiMember(Name = "Country", Description = "Country", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
  494. public string Country { get; set; }
  495. }
  496. [Route("/LiveTv/ListingProviders/SchedulesDirect/Countries", "GET", Summary = "Gets available lineups")]
  497. [Authenticated]
  498. public class GetSchedulesDirectCountries
  499. {
  500. }
  501. [Route("/LiveTv/ChannelMappingOptions")]
  502. [Authenticated]
  503. public class GetChannelMappingOptions
  504. {
  505. [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")]
  506. public string ProviderId { get; set; }
  507. }
  508. [Route("/LiveTv/ChannelMappings")]
  509. [Authenticated]
  510. public class SetChannelMapping
  511. {
  512. [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")]
  513. public string ProviderId { get; set; }
  514. public string TunerChannelId { get; set; }
  515. public string ProviderChannelId { get; set; }
  516. }
  517. public class ChannelMappingOptions
  518. {
  519. public List<TunerChannelMapping> TunerChannels { get; set; }
  520. public List<NameIdPair> ProviderChannels { get; set; }
  521. public NameValuePair[] Mappings { get; set; }
  522. public string ProviderName { get; set; }
  523. }
  524. [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
  525. public class GetLiveStreamFile
  526. {
  527. public string Id { get; set; }
  528. public string Container { get; set; }
  529. }
  530. [Route("/LiveTv/LiveRecordings/{Id}/stream", "GET", Summary = "Gets a live tv channel")]
  531. public class GetLiveRecordingFile
  532. {
  533. public string Id { get; set; }
  534. }
  535. [Route("/LiveTv/TunerHosts/Types", "GET")]
  536. [Authenticated]
  537. public class GetTunerHostTypes : IReturn<List<NameIdPair>>
  538. {
  539. }
  540. [Route("/LiveTv/Tuners/Discvover", "GET")]
  541. [Authenticated]
  542. public class DiscoverTuners : IReturn<List<TunerHostInfo>>
  543. {
  544. public bool NewDevicesOnly { get; set; }
  545. }
  546. public class LiveTvService : BaseApiService
  547. {
  548. private readonly ILiveTvManager _liveTvManager;
  549. private readonly IUserManager _userManager;
  550. private readonly IHttpClient _httpClient;
  551. private readonly ILibraryManager _libraryManager;
  552. private readonly IDtoService _dtoService;
  553. private readonly IAuthorizationContext _authContext;
  554. private readonly ISessionContext _sessionContext;
  555. private readonly IStreamHelper _streamHelper;
  556. private readonly IMediaSourceManager _mediaSourceManager;
  557. public LiveTvService(
  558. ILogger<LiveTvService> logger,
  559. IServerConfigurationManager serverConfigurationManager,
  560. IHttpResultFactory httpResultFactory,
  561. IMediaSourceManager mediaSourceManager,
  562. IStreamHelper streamHelper,
  563. ILiveTvManager liveTvManager,
  564. IUserManager userManager,
  565. IHttpClient httpClient,
  566. ILibraryManager libraryManager,
  567. IDtoService dtoService,
  568. IAuthorizationContext authContext,
  569. ISessionContext sessionContext)
  570. : base(logger, serverConfigurationManager, httpResultFactory)
  571. {
  572. _mediaSourceManager = mediaSourceManager;
  573. _streamHelper = streamHelper;
  574. _liveTvManager = liveTvManager;
  575. _userManager = userManager;
  576. _httpClient = httpClient;
  577. _libraryManager = libraryManager;
  578. _dtoService = dtoService;
  579. _authContext = authContext;
  580. _sessionContext = sessionContext;
  581. }
  582. public object Get(GetTunerHostTypes request)
  583. {
  584. var list = _liveTvManager.GetTunerHostTypes();
  585. return ToOptimizedResult(list);
  586. }
  587. public object Get(GetRecordingFolders request)
  588. {
  589. var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
  590. var folders = _liveTvManager.GetRecordingFolders(user);
  591. var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user);
  592. var result = new QueryResult<BaseItemDto>
  593. {
  594. Items = returnArray,
  595. TotalRecordCount = returnArray.Count
  596. };
  597. return ToOptimizedResult(result);
  598. }
  599. public object Get(GetLiveRecordingFile request)
  600. {
  601. var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id);
  602. if (string.IsNullOrWhiteSpace(path))
  603. {
  604. throw new FileNotFoundException();
  605. }
  606. var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  607. {
  608. [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType(path)
  609. };
  610. return new ProgressiveFileCopier(_streamHelper, path, outputHeaders, Logger)
  611. {
  612. AllowEndOfFile = false
  613. };
  614. }
  615. public async Task<object> Get(DiscoverTuners request)
  616. {
  617. var result = await _liveTvManager.DiscoverTuners(request.NewDevicesOnly, CancellationToken.None).ConfigureAwait(false);
  618. return ToOptimizedResult(result);
  619. }
  620. public async Task<object> Get(GetLiveStreamFile request)
  621. {
  622. var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(request.Id, CancellationToken.None).ConfigureAwait(false);
  623. var directStreamProvider = liveStreamInfo;
  624. var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  625. {
  626. [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType("file." + request.Container)
  627. };
  628. return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger)
  629. {
  630. AllowEndOfFile = false
  631. };
  632. }
  633. public object Get(GetDefaultListingProvider request)
  634. {
  635. return ToOptimizedResult(new ListingsProviderInfo());
  636. }
  637. public async Task<object> Post(SetChannelMapping request)
  638. {
  639. return await _liveTvManager.SetChannelMapping(request.ProviderId, request.TunerChannelId, request.ProviderChannelId).ConfigureAwait(false);
  640. }
  641. public async Task<object> Get(GetChannelMappingOptions request)
  642. {
  643. var config = GetConfiguration();
  644. var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(request.ProviderId, i.Id, StringComparison.OrdinalIgnoreCase));
  645. var listingsProviderName = _liveTvManager.ListingProviders.First(i => string.Equals(i.Type, listingsProviderInfo.Type, StringComparison.OrdinalIgnoreCase)).Name;
  646. var tunerChannels = await _liveTvManager.GetChannelsForListingsProvider(request.ProviderId, CancellationToken.None)
  647. .ConfigureAwait(false);
  648. var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(request.ProviderId, CancellationToken.None)
  649. .ConfigureAwait(false);
  650. var mappings = listingsProviderInfo.ChannelMappings;
  651. var result = new ChannelMappingOptions
  652. {
  653. TunerChannels = tunerChannels.Select(i => _liveTvManager.GetTunerChannelMapping(i, mappings, providerChannels)).ToList(),
  654. ProviderChannels = providerChannels.Select(i => new NameIdPair
  655. {
  656. Name = i.Name,
  657. Id = i.Id
  658. }).ToList(),
  659. Mappings = mappings,
  660. ProviderName = listingsProviderName
  661. };
  662. return ToOptimizedResult(result);
  663. }
  664. public async Task<object> Get(GetSchedulesDirectCountries request)
  665. {
  666. // https://json.schedulesdirect.org/20141201/available/countries
  667. var response = await _httpClient.Get(new HttpRequestOptions
  668. {
  669. Url = "https://json.schedulesdirect.org/20141201/available/countries",
  670. BufferContent = false
  671. }).ConfigureAwait(false);
  672. return ResultFactory.GetResult(Request, response, "application/json");
  673. }
  674. private void AssertUserCanManageLiveTv()
  675. {
  676. var user = _sessionContext.GetUser(Request);
  677. if (user == null)
  678. {
  679. throw new SecurityException("Anonymous live tv management is not allowed.");
  680. }
  681. if (!user.HasPermission(PermissionKind.EnableLiveTvManagement))
  682. {
  683. throw new SecurityException("The current user does not have permission to manage live tv.");
  684. }
  685. }
  686. public async Task<object> Post(AddListingProvider request)
  687. {
  688. if (request.Pw != null)
  689. {
  690. request.Password = GetHashedString(request.Pw);
  691. }
  692. request.Pw = null;
  693. var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false);
  694. return ToOptimizedResult(result);
  695. }
  696. /// <summary>
  697. /// Gets the hashed string.
  698. /// </summary>
  699. private string GetHashedString(string str)
  700. {
  701. // SchedulesDirect requires a SHA1 hash of the user's password
  702. // https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token
  703. using SHA1 sha = SHA1.Create();
  704. return Hex.Encode(
  705. sha.ComputeHash(Encoding.UTF8.GetBytes(str)));
  706. }
  707. public void Delete(DeleteListingProvider request)
  708. {
  709. _liveTvManager.DeleteListingsProvider(request.Id);
  710. }
  711. public async Task<object> Post(AddTunerHost request)
  712. {
  713. var result = await _liveTvManager.SaveTunerHost(request).ConfigureAwait(false);
  714. return ToOptimizedResult(result);
  715. }
  716. public void Delete(DeleteTunerHost request)
  717. {
  718. var config = GetConfiguration();
  719. config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
  720. ServerConfigurationManager.SaveConfiguration("livetv", config);
  721. }
  722. private LiveTvOptions GetConfiguration()
  723. {
  724. return ServerConfigurationManager.GetConfiguration<LiveTvOptions>("livetv");
  725. }
  726. private void UpdateConfiguration(LiveTvOptions options)
  727. {
  728. ServerConfigurationManager.SaveConfiguration("livetv", options);
  729. }
  730. public async Task<object> Get(GetLineups request)
  731. {
  732. var info = await _liveTvManager.GetLineups(request.Type, request.Id, request.Country, request.Location).ConfigureAwait(false);
  733. return ToOptimizedResult(info);
  734. }
  735. public object Get(GetLiveTvInfo request)
  736. {
  737. var info = _liveTvManager.GetLiveTvInfo(CancellationToken.None);
  738. return ToOptimizedResult(info);
  739. }
  740. public object Get(GetChannels request)
  741. {
  742. var options = GetDtoOptions(_authContext, request);
  743. var channelResult = _liveTvManager.GetInternalChannels(new LiveTvChannelQuery
  744. {
  745. ChannelType = request.Type,
  746. UserId = request.UserId,
  747. StartIndex = request.StartIndex,
  748. Limit = request.Limit,
  749. IsFavorite = request.IsFavorite,
  750. IsLiked = request.IsLiked,
  751. IsDisliked = request.IsDisliked,
  752. EnableFavoriteSorting = request.EnableFavoriteSorting,
  753. IsMovie = request.IsMovie,
  754. IsSeries = request.IsSeries,
  755. IsNews = request.IsNews,
  756. IsKids = request.IsKids,
  757. IsSports = request.IsSports,
  758. SortBy = request.GetOrderBy(),
  759. SortOrder = request.SortOrder ?? SortOrder.Ascending,
  760. AddCurrentProgram = request.AddCurrentProgram
  761. }, options, CancellationToken.None);
  762. var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
  763. RemoveFields(options);
  764. options.AddCurrentProgram = request.AddCurrentProgram;
  765. var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, options, user);
  766. var result = new QueryResult<BaseItemDto>
  767. {
  768. Items = returnArray,
  769. TotalRecordCount = channelResult.TotalRecordCount
  770. };
  771. return ToOptimizedResult(result);
  772. }
  773. private void RemoveFields(DtoOptions options)
  774. {
  775. var fields = options.Fields.ToList();
  776. fields.Remove(ItemFields.CanDelete);
  777. fields.Remove(ItemFields.CanDownload);
  778. fields.Remove(ItemFields.DisplayPreferencesId);
  779. fields.Remove(ItemFields.Etag);
  780. options.Fields = fields.ToArray();
  781. }
  782. public object Get(GetChannel request)
  783. {
  784. var user = _userManager.GetUserById(request.UserId);
  785. var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
  786. var dtoOptions = GetDtoOptions(_authContext, request);
  787. var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
  788. return ToOptimizedResult(result);
  789. }
  790. public async Task<object> Get(GetPrograms request)
  791. {
  792. var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
  793. var query = new InternalItemsQuery(user)
  794. {
  795. ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true).Select(i => new Guid(i)).ToArray(),
  796. HasAired = request.HasAired,
  797. IsAiring = request.IsAiring,
  798. EnableTotalRecordCount = request.EnableTotalRecordCount
  799. };
  800. if (!string.IsNullOrEmpty(request.MinStartDate))
  801. {
  802. query.MinStartDate = DateTime.Parse(request.MinStartDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
  803. }
  804. if (!string.IsNullOrEmpty(request.MinEndDate))
  805. {
  806. query.MinEndDate = DateTime.Parse(request.MinEndDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
  807. }
  808. if (!string.IsNullOrEmpty(request.MaxStartDate))
  809. {
  810. query.MaxStartDate = DateTime.Parse(request.MaxStartDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
  811. }
  812. if (!string.IsNullOrEmpty(request.MaxEndDate))
  813. {
  814. query.MaxEndDate = DateTime.Parse(request.MaxEndDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
  815. }
  816. query.StartIndex = request.StartIndex;
  817. query.Limit = request.Limit;
  818. query.OrderBy = BaseItemsRequest.GetOrderBy(request.SortBy, request.SortOrder);
  819. query.IsNews = request.IsNews;
  820. query.IsMovie = request.IsMovie;
  821. query.IsSeries = request.IsSeries;
  822. query.IsKids = request.IsKids;
  823. query.IsSports = request.IsSports;
  824. query.SeriesTimerId = request.SeriesTimerId;
  825. query.Genres = (request.Genres ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
  826. query.GenreIds = GetGuids(request.GenreIds);
  827. if (!request.LibrarySeriesId.Equals(Guid.Empty))
  828. {
  829. query.IsSeries = true;
  830. if (_libraryManager.GetItemById(request.LibrarySeriesId) is Series series)
  831. {
  832. query.Name = series.Name;
  833. }
  834. }
  835. var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None).ConfigureAwait(false);
  836. return ToOptimizedResult(result);
  837. }
  838. public object Get(GetRecommendedPrograms request)
  839. {
  840. var user = _userManager.GetUserById(request.UserId);
  841. var query = new InternalItemsQuery(user)
  842. {
  843. IsAiring = request.IsAiring,
  844. Limit = request.Limit,
  845. HasAired = request.HasAired,
  846. IsSeries = request.IsSeries,
  847. IsMovie = request.IsMovie,
  848. IsKids = request.IsKids,
  849. IsNews = request.IsNews,
  850. IsSports = request.IsSports,
  851. EnableTotalRecordCount = request.EnableTotalRecordCount
  852. };
  853. query.GenreIds = GetGuids(request.GenreIds);
  854. var result = _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None);
  855. return ToOptimizedResult(result);
  856. }
  857. public object Post(GetPrograms request)
  858. {
  859. return Get(request);
  860. }
  861. public object Get(GetRecordings request)
  862. {
  863. var options = GetDtoOptions(_authContext, request);
  864. var result = _liveTvManager.GetRecordings(new RecordingQuery
  865. {
  866. ChannelId = request.ChannelId,
  867. UserId = request.UserId,
  868. StartIndex = request.StartIndex,
  869. Limit = request.Limit,
  870. Status = request.Status,
  871. SeriesTimerId = request.SeriesTimerId,
  872. IsInProgress = request.IsInProgress,
  873. EnableTotalRecordCount = request.EnableTotalRecordCount,
  874. IsMovie = request.IsMovie,
  875. IsNews = request.IsNews,
  876. IsSeries = request.IsSeries,
  877. IsKids = request.IsKids,
  878. IsSports = request.IsSports,
  879. IsLibraryItem = request.IsLibraryItem,
  880. Fields = request.GetItemFields(),
  881. ImageTypeLimit = request.ImageTypeLimit,
  882. EnableImages = request.EnableImages
  883. }, options);
  884. return ToOptimizedResult(result);
  885. }
  886. public object Get(GetRecordingSeries request)
  887. {
  888. return ToOptimizedResult(new QueryResult<BaseItemDto>());
  889. }
  890. public object Get(GetRecording request)
  891. {
  892. var user = _userManager.GetUserById(request.UserId);
  893. var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
  894. var dtoOptions = GetDtoOptions(_authContext, request);
  895. var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
  896. return ToOptimizedResult(result);
  897. }
  898. public async Task<object> Get(GetTimer request)
  899. {
  900. var result = await _liveTvManager.GetTimer(request.Id, CancellationToken.None).ConfigureAwait(false);
  901. return ToOptimizedResult(result);
  902. }
  903. public async Task<object> Get(GetTimers request)
  904. {
  905. var result = await _liveTvManager.GetTimers(new TimerQuery
  906. {
  907. ChannelId = request.ChannelId,
  908. SeriesTimerId = request.SeriesTimerId,
  909. IsActive = request.IsActive,
  910. IsScheduled = request.IsScheduled
  911. }, CancellationToken.None).ConfigureAwait(false);
  912. return ToOptimizedResult(result);
  913. }
  914. public void Delete(DeleteRecording request)
  915. {
  916. AssertUserCanManageLiveTv();
  917. _libraryManager.DeleteItem(_libraryManager.GetItemById(request.Id), new DeleteOptions
  918. {
  919. DeleteFileLocation = false
  920. });
  921. }
  922. public Task Delete(CancelTimer request)
  923. {
  924. AssertUserCanManageLiveTv();
  925. return _liveTvManager.CancelTimer(request.Id);
  926. }
  927. public Task Post(UpdateTimer request)
  928. {
  929. AssertUserCanManageLiveTv();
  930. return _liveTvManager.UpdateTimer(request, CancellationToken.None);
  931. }
  932. public async Task<object> Get(GetSeriesTimers request)
  933. {
  934. var result = await _liveTvManager.GetSeriesTimers(new SeriesTimerQuery
  935. {
  936. SortOrder = request.SortOrder,
  937. SortBy = request.SortBy
  938. }, CancellationToken.None).ConfigureAwait(false);
  939. return ToOptimizedResult(result);
  940. }
  941. public async Task<object> Get(GetSeriesTimer request)
  942. {
  943. var result = await _liveTvManager.GetSeriesTimer(request.Id, CancellationToken.None).ConfigureAwait(false);
  944. return ToOptimizedResult(result);
  945. }
  946. public Task Delete(CancelSeriesTimer request)
  947. {
  948. AssertUserCanManageLiveTv();
  949. return _liveTvManager.CancelSeriesTimer(request.Id);
  950. }
  951. public Task Post(UpdateSeriesTimer request)
  952. {
  953. AssertUserCanManageLiveTv();
  954. return _liveTvManager.UpdateSeriesTimer(request, CancellationToken.None);
  955. }
  956. public async Task<object> Get(GetDefaultTimer request)
  957. {
  958. if (string.IsNullOrEmpty(request.ProgramId))
  959. {
  960. var result = await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false);
  961. return ToOptimizedResult(result);
  962. }
  963. else
  964. {
  965. var result = await _liveTvManager.GetNewTimerDefaults(request.ProgramId, CancellationToken.None).ConfigureAwait(false);
  966. return ToOptimizedResult(result);
  967. }
  968. }
  969. public async Task<object> Get(GetProgram request)
  970. {
  971. var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
  972. var result = await _liveTvManager.GetProgram(request.Id, CancellationToken.None, user).ConfigureAwait(false);
  973. return ToOptimizedResult(result);
  974. }
  975. public Task Post(CreateSeriesTimer request)
  976. {
  977. AssertUserCanManageLiveTv();
  978. return _liveTvManager.CreateSeriesTimer(request, CancellationToken.None);
  979. }
  980. public Task Post(CreateTimer request)
  981. {
  982. AssertUserCanManageLiveTv();
  983. return _liveTvManager.CreateTimer(request, CancellationToken.None);
  984. }
  985. public object Get(GetRecordingGroups request)
  986. {
  987. return ToOptimizedResult(new QueryResult<BaseItemDto>());
  988. }
  989. public object Get(GetRecordingGroup request)
  990. {
  991. throw new FileNotFoundException();
  992. }
  993. public object Get(GetGuideInfo request)
  994. {
  995. return ToOptimizedResult(_liveTvManager.GetGuideInfo());
  996. }
  997. public Task Post(ResetTuner request)
  998. {
  999. AssertUserCanManageLiveTv();
  1000. return _liveTvManager.ResetTuner(request.Id, CancellationToken.None);
  1001. }
  1002. }
  1003. }