2
0

LiveTvManager.cs 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605
  1. using MediaBrowser.Common.Configuration;
  2. using MediaBrowser.Common.Extensions;
  3. using MediaBrowser.Common.Progress;
  4. using MediaBrowser.Controller.Configuration;
  5. using MediaBrowser.Controller.Drawing;
  6. using MediaBrowser.Controller.Dto;
  7. using MediaBrowser.Controller.Entities;
  8. using MediaBrowser.Controller.Library;
  9. using MediaBrowser.Controller.LiveTv;
  10. using MediaBrowser.Controller.Persistence;
  11. using MediaBrowser.Controller.Providers;
  12. using MediaBrowser.Controller.Sorting;
  13. using MediaBrowser.Model.Dto;
  14. using MediaBrowser.Model.Entities;
  15. using MediaBrowser.Model.LiveTv;
  16. using Microsoft.Extensions.Logging;
  17. using MediaBrowser.Model.Querying;
  18. using MediaBrowser.Model.Serialization;
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Linq;
  22. using System.Threading;
  23. using System.Threading.Tasks;
  24. using MediaBrowser.Model.IO;
  25. using MediaBrowser.Common.Events;
  26. using MediaBrowser.Common.Security;
  27. using MediaBrowser.Controller.Entities.Movies;
  28. using MediaBrowser.Controller.Entities.TV;
  29. using MediaBrowser.Model.Events;
  30. using MediaBrowser.Model.Extensions;
  31. using MediaBrowser.Model.Globalization;
  32. using MediaBrowser.Model.Tasks;
  33. using Emby.Server.Implementations.LiveTv.Listings;
  34. using MediaBrowser.Controller.Channels;
  35. using Emby.Server.Implementations.Library;
  36. using MediaBrowser.Controller;
  37. using MediaBrowser.Common.Net;
  38. namespace Emby.Server.Implementations.LiveTv
  39. {
  40. /// <summary>
  41. /// Class LiveTvManager
  42. /// </summary>
  43. public class LiveTvManager : ILiveTvManager, IDisposable
  44. {
  45. private readonly IServerConfigurationManager _config;
  46. private readonly ILogger _logger;
  47. private readonly IItemRepository _itemRepo;
  48. private readonly IUserManager _userManager;
  49. private readonly IUserDataManager _userDataManager;
  50. private readonly ILibraryManager _libraryManager;
  51. private readonly ITaskManager _taskManager;
  52. private readonly IJsonSerializer _jsonSerializer;
  53. private readonly IProviderManager _providerManager;
  54. private readonly ISecurityManager _security;
  55. private readonly Func<IChannelManager> _channelManager;
  56. private readonly IDtoService _dtoService;
  57. private readonly ILocalizationManager _localization;
  58. private readonly LiveTvDtoService _tvDtoService;
  59. private ILiveTvService[] _services = new ILiveTvService[] { };
  60. private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
  61. private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
  62. private readonly IFileSystem _fileSystem;
  63. public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
  64. public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
  65. public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
  66. public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
  67. public string GetEmbyTvActiveRecordingPath(string id)
  68. {
  69. return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
  70. }
  71. private IServerApplicationHost _appHost;
  72. private IHttpClient _httpClient;
  73. public LiveTvManager(IServerApplicationHost appHost, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security, Func<IChannelManager> channelManager)
  74. {
  75. _appHost = appHost;
  76. _config = config;
  77. _logger = logger;
  78. _itemRepo = itemRepo;
  79. _userManager = userManager;
  80. _libraryManager = libraryManager;
  81. _taskManager = taskManager;
  82. _localization = localization;
  83. _jsonSerializer = jsonSerializer;
  84. _providerManager = providerManager;
  85. _fileSystem = fileSystem;
  86. _security = security;
  87. _dtoService = dtoService;
  88. _userDataManager = userDataManager;
  89. _channelManager = channelManager;
  90. _httpClient = httpClient;
  91. _tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, logger, appHost, _libraryManager);
  92. }
  93. /// <summary>
  94. /// Gets the services.
  95. /// </summary>
  96. /// <value>The services.</value>
  97. public IReadOnlyList<ILiveTvService> Services
  98. {
  99. get { return _services; }
  100. }
  101. private LiveTvOptions GetConfiguration()
  102. {
  103. return _config.GetConfiguration<LiveTvOptions>("livetv");
  104. }
  105. /// <summary>
  106. /// Adds the parts.
  107. /// </summary>
  108. /// <param name="services">The services.</param>
  109. /// <param name="tunerHosts">The tuner hosts.</param>
  110. /// <param name="listingProviders">The listing providers.</param>
  111. public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
  112. {
  113. _services = services.ToArray();
  114. _tunerHosts = tunerHosts.Where(i => i.IsSupported).ToArray();
  115. _listingProviders = listingProviders.ToArray();
  116. foreach (var service in _services)
  117. {
  118. service.DataSourceChanged += service_DataSourceChanged;
  119. var embyTv = service as EmbyTV.EmbyTV;
  120. if (embyTv != null)
  121. {
  122. embyTv.TimerCreated += EmbyTv_TimerCreated;
  123. embyTv.TimerCancelled += EmbyTv_TimerCancelled;
  124. }
  125. }
  126. }
  127. private void EmbyTv_TimerCancelled(object sender, GenericEventArgs<string> e)
  128. {
  129. var timerId = e.Argument;
  130. EventHelper.FireEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
  131. {
  132. Argument = new TimerEventInfo
  133. {
  134. Id = timerId
  135. }
  136. }, _logger);
  137. }
  138. private void EmbyTv_TimerCreated(object sender, GenericEventArgs<TimerInfo> e)
  139. {
  140. var timer = e.Argument;
  141. var service = sender as ILiveTvService;
  142. EventHelper.FireEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
  143. {
  144. Argument = new TimerEventInfo
  145. {
  146. ProgramId = _tvDtoService.GetInternalProgramId(timer.ProgramId),
  147. Id = timer.Id
  148. }
  149. }, _logger);
  150. }
  151. public ITunerHost[] TunerHosts
  152. {
  153. get { return _tunerHosts; }
  154. }
  155. public IListingsProvider[] ListingProviders
  156. {
  157. get { return _listingProviders; }
  158. }
  159. public List<NameIdPair> GetTunerHostTypes()
  160. {
  161. return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
  162. {
  163. Name = i.Name,
  164. Id = i.Type
  165. }).ToList();
  166. }
  167. public Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
  168. {
  169. return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken);
  170. }
  171. void service_DataSourceChanged(object sender, EventArgs e)
  172. {
  173. if (!_isDisposed)
  174. {
  175. _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
  176. }
  177. }
  178. public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
  179. {
  180. var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
  181. var topFolder = GetInternalLiveTvFolder(cancellationToken);
  182. var internalQuery = new InternalItemsQuery(user)
  183. {
  184. IsMovie = query.IsMovie,
  185. IsNews = query.IsNews,
  186. IsKids = query.IsKids,
  187. IsSports = query.IsSports,
  188. IsSeries = query.IsSeries,
  189. IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
  190. TopParentIds = new[] { topFolder.Id },
  191. IsFavorite = query.IsFavorite,
  192. IsLiked = query.IsLiked,
  193. StartIndex = query.StartIndex,
  194. Limit = query.Limit,
  195. DtoOptions = dtoOptions
  196. };
  197. var orderBy = internalQuery.OrderBy.ToList();
  198. orderBy.AddRange(query.SortBy.Select(i => new ValueTuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
  199. if (query.EnableFavoriteSorting)
  200. {
  201. orderBy.Insert(0, new ValueTuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
  202. }
  203. if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
  204. {
  205. orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
  206. }
  207. internalQuery.OrderBy = orderBy.ToArray();
  208. return _libraryManager.GetItemsResult(internalQuery);
  209. }
  210. public async Task<Tuple<MediaSourceInfo, ILiveStream>> GetChannelStream(string id, string mediaSourceId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
  211. {
  212. if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
  213. {
  214. mediaSourceId = null;
  215. }
  216. MediaSourceInfo info;
  217. bool isVideo;
  218. ILiveTvService service;
  219. ILiveStream liveStream;
  220. var channel = (LiveTvChannel)_libraryManager.GetItemById(id);
  221. isVideo = channel.ChannelType == ChannelType.TV;
  222. service = GetService(channel);
  223. _logger.LogInformation("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
  224. var supportsManagedStream = service as ISupportsDirectStreamProvider;
  225. if (supportsManagedStream != null)
  226. {
  227. liveStream = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
  228. info = liveStream.MediaSource;
  229. }
  230. else
  231. {
  232. info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
  233. var openedId = info.Id;
  234. Func<Task> closeFn = () => service.CloseLiveStream(openedId, CancellationToken.None);
  235. liveStream = new ExclusiveLiveStream(info, closeFn);
  236. var startTime = DateTime.UtcNow;
  237. await liveStream.Open(cancellationToken).ConfigureAwait(false);
  238. var endTime = DateTime.UtcNow;
  239. _logger.LogInformation("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds);
  240. }
  241. info.RequiresClosing = true;
  242. var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
  243. info.LiveStreamId = idPrefix + info.Id;
  244. Normalize(info, service, isVideo);
  245. return new Tuple<MediaSourceInfo, ILiveStream>(info, liveStream);
  246. }
  247. public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(BaseItem item, CancellationToken cancellationToken)
  248. {
  249. var baseItem = (LiveTvChannel)item;
  250. var service = GetService(baseItem);
  251. var sources = await service.GetChannelStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
  252. if (sources.Count == 0)
  253. {
  254. throw new NotImplementedException();
  255. }
  256. var list = sources.ToList();
  257. foreach (var source in list)
  258. {
  259. Normalize(source, service, baseItem.ChannelType == ChannelType.TV);
  260. }
  261. return list;
  262. }
  263. private ILiveTvService GetService(LiveTvChannel item)
  264. {
  265. var name = item.ServiceName;
  266. return GetService(name);
  267. }
  268. private ILiveTvService GetService(LiveTvProgram item)
  269. {
  270. var channel = _libraryManager.GetItemById(item.ChannelId) as LiveTvChannel;
  271. return GetService(channel);
  272. }
  273. private ILiveTvService GetService(string name)
  274. {
  275. return _services.FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
  276. }
  277. private void Normalize(MediaSourceInfo mediaSource, ILiveTvService service, bool isVideo)
  278. {
  279. // Not all of the plugins are setting this
  280. mediaSource.IsInfiniteStream = true;
  281. if (mediaSource.MediaStreams.Count == 0)
  282. {
  283. if (isVideo)
  284. {
  285. mediaSource.MediaStreams.AddRange(new List<MediaStream>
  286. {
  287. new MediaStream
  288. {
  289. Type = MediaStreamType.Video,
  290. // Set the index to -1 because we don't know the exact index of the video stream within the container
  291. Index = -1,
  292. // Set to true if unknown to enable deinterlacing
  293. IsInterlaced = true
  294. },
  295. new MediaStream
  296. {
  297. Type = MediaStreamType.Audio,
  298. // Set the index to -1 because we don't know the exact index of the audio stream within the container
  299. Index = -1
  300. }
  301. });
  302. }
  303. else
  304. {
  305. mediaSource.MediaStreams.AddRange(new List<MediaStream>
  306. {
  307. new MediaStream
  308. {
  309. Type = MediaStreamType.Audio,
  310. // Set the index to -1 because we don't know the exact index of the audio stream within the container
  311. Index = -1
  312. }
  313. });
  314. }
  315. }
  316. // Clean some bad data coming from providers
  317. foreach (var stream in mediaSource.MediaStreams)
  318. {
  319. if (stream.BitRate.HasValue && stream.BitRate <= 0)
  320. {
  321. stream.BitRate = null;
  322. }
  323. if (stream.Channels.HasValue && stream.Channels <= 0)
  324. {
  325. stream.Channels = null;
  326. }
  327. if (stream.AverageFrameRate.HasValue && stream.AverageFrameRate <= 0)
  328. {
  329. stream.AverageFrameRate = null;
  330. }
  331. if (stream.RealFrameRate.HasValue && stream.RealFrameRate <= 0)
  332. {
  333. stream.RealFrameRate = null;
  334. }
  335. if (stream.Width.HasValue && stream.Width <= 0)
  336. {
  337. stream.Width = null;
  338. }
  339. if (stream.Height.HasValue && stream.Height <= 0)
  340. {
  341. stream.Height = null;
  342. }
  343. if (stream.SampleRate.HasValue && stream.SampleRate <= 0)
  344. {
  345. stream.SampleRate = null;
  346. }
  347. if (stream.Level.HasValue && stream.Level <= 0)
  348. {
  349. stream.Level = null;
  350. }
  351. }
  352. var indexes = mediaSource.MediaStreams.Select(i => i.Index).Distinct().ToList();
  353. // If there are duplicate stream indexes, set them all to unknown
  354. if (indexes.Count != mediaSource.MediaStreams.Count)
  355. {
  356. foreach (var stream in mediaSource.MediaStreams)
  357. {
  358. stream.Index = -1;
  359. }
  360. }
  361. // Set the total bitrate if not already supplied
  362. mediaSource.InferTotalBitrate();
  363. if (!(service is EmbyTV.EmbyTV))
  364. {
  365. // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
  366. //mediaSource.SupportsDirectPlay = false;
  367. //mediaSource.SupportsDirectStream = false;
  368. mediaSource.SupportsTranscoding = true;
  369. foreach (var stream in mediaSource.MediaStreams)
  370. {
  371. if (stream.Type == MediaStreamType.Video && string.IsNullOrWhiteSpace(stream.NalLengthSize))
  372. {
  373. stream.NalLengthSize = "0";
  374. }
  375. if (stream.Type == MediaStreamType.Video)
  376. {
  377. stream.IsInterlaced = true;
  378. }
  379. }
  380. }
  381. }
  382. private const string ExternalServiceTag = "ExternalServiceId";
  383. private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
  384. {
  385. var parentFolderId = parentFolder.Id;
  386. var isNew = false;
  387. var forceUpdate = false;
  388. var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id);
  389. var item = _libraryManager.GetItemById(id) as LiveTvChannel;
  390. if (item == null)
  391. {
  392. item = new LiveTvChannel
  393. {
  394. Name = channelInfo.Name,
  395. Id = id,
  396. DateCreated = DateTime.UtcNow
  397. };
  398. isNew = true;
  399. }
  400. if (channelInfo.Tags != null)
  401. {
  402. if (!channelInfo.Tags.SequenceEqual(item.Tags, StringComparer.OrdinalIgnoreCase))
  403. {
  404. isNew = true;
  405. }
  406. item.Tags = channelInfo.Tags;
  407. }
  408. if (!item.ParentId.Equals(parentFolderId))
  409. {
  410. isNew = true;
  411. }
  412. item.ParentId = parentFolderId;
  413. item.ChannelType = channelInfo.ChannelType;
  414. item.ServiceName = serviceName;
  415. if (!string.Equals(item.GetProviderId(ExternalServiceTag), serviceName, StringComparison.OrdinalIgnoreCase))
  416. {
  417. forceUpdate = true;
  418. }
  419. item.SetProviderId(ExternalServiceTag, serviceName);
  420. if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
  421. {
  422. forceUpdate = true;
  423. }
  424. item.ExternalId = channelInfo.Id;
  425. if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal))
  426. {
  427. forceUpdate = true;
  428. }
  429. item.Number = channelInfo.Number;
  430. if (!string.Equals(channelInfo.Name, item.Name, StringComparison.Ordinal))
  431. {
  432. forceUpdate = true;
  433. }
  434. item.Name = channelInfo.Name;
  435. if (!item.HasImage(ImageType.Primary))
  436. {
  437. if (!string.IsNullOrWhiteSpace(channelInfo.ImagePath))
  438. {
  439. item.SetImagePath(ImageType.Primary, channelInfo.ImagePath);
  440. forceUpdate = true;
  441. }
  442. else if (!string.IsNullOrWhiteSpace(channelInfo.ImageUrl))
  443. {
  444. item.SetImagePath(ImageType.Primary, channelInfo.ImageUrl);
  445. forceUpdate = true;
  446. }
  447. }
  448. if (isNew)
  449. {
  450. _libraryManager.CreateItem(item, parentFolder);
  451. }
  452. else if (forceUpdate)
  453. {
  454. _libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
  455. }
  456. return item;
  457. }
  458. private const string EtagKey = "ProgramEtag";
  459. private Tuple<LiveTvProgram, bool, bool> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
  460. {
  461. var id = _tvDtoService.GetInternalProgramId(info.Id);
  462. LiveTvProgram item = null;
  463. allExistingPrograms.TryGetValue(id, out item);
  464. var isNew = false;
  465. var forceUpdate = false;
  466. if (item == null)
  467. {
  468. isNew = true;
  469. item = new LiveTvProgram
  470. {
  471. Name = info.Name,
  472. Id = id,
  473. DateCreated = DateTime.UtcNow,
  474. DateModified = DateTime.UtcNow
  475. };
  476. if (!string.IsNullOrEmpty(info.Etag))
  477. {
  478. item.SetProviderId(EtagKey, info.Etag);
  479. }
  480. }
  481. if (!string.Equals(info.ShowId, item.ShowId, StringComparison.OrdinalIgnoreCase))
  482. {
  483. item.ShowId = info.ShowId;
  484. forceUpdate = true;
  485. }
  486. var seriesId = info.SeriesId;
  487. if (!item.ParentId.Equals(channel.Id))
  488. {
  489. forceUpdate = true;
  490. }
  491. item.ParentId = channel.Id;
  492. //item.ChannelType = channelType;
  493. item.Audio = info.Audio;
  494. item.ChannelId = channel.Id;
  495. item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
  496. if ((item.CommunityRating ?? 0).Equals(0))
  497. {
  498. item.CommunityRating = null;
  499. }
  500. item.EpisodeTitle = info.EpisodeTitle;
  501. item.ExternalId = info.Id;
  502. if (!string.IsNullOrWhiteSpace(seriesId) && !string.Equals(item.ExternalSeriesId, seriesId, StringComparison.Ordinal))
  503. {
  504. forceUpdate = true;
  505. }
  506. item.ExternalSeriesId = seriesId;
  507. var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
  508. if (isSeries || !string.IsNullOrEmpty(info.EpisodeTitle))
  509. {
  510. item.SeriesName = info.Name;
  511. }
  512. var tags = new List<string>();
  513. if (info.IsLive)
  514. {
  515. tags.Add("Live");
  516. }
  517. if (info.IsPremiere)
  518. {
  519. tags.Add("Premiere");
  520. }
  521. if (info.IsNews)
  522. {
  523. tags.Add("News");
  524. }
  525. if (info.IsSports)
  526. {
  527. tags.Add("Sports");
  528. }
  529. if (info.IsKids)
  530. {
  531. tags.Add("Kids");
  532. }
  533. if (info.IsRepeat)
  534. {
  535. tags.Add("Repeat");
  536. }
  537. if (info.IsMovie)
  538. {
  539. tags.Add("Movie");
  540. }
  541. if (isSeries)
  542. {
  543. tags.Add("Series");
  544. }
  545. item.Tags = tags.ToArray();
  546. item.Genres = info.Genres.ToArray();
  547. if (info.IsHD ?? false)
  548. {
  549. item.Width = 1280;
  550. item.Height = 720;
  551. }
  552. item.IsMovie = info.IsMovie;
  553. item.IsRepeat = info.IsRepeat;
  554. if (item.IsSeries != isSeries)
  555. {
  556. forceUpdate = true;
  557. }
  558. item.IsSeries = isSeries;
  559. item.Name = info.Name;
  560. item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
  561. item.Overview = item.Overview ?? info.Overview;
  562. item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
  563. item.ProviderIds = info.ProviderIds;
  564. foreach (var providerId in info.SeriesProviderIds)
  565. {
  566. info.ProviderIds["Series" + providerId.Key] = providerId.Value;
  567. }
  568. if (item.StartDate != info.StartDate)
  569. {
  570. forceUpdate = true;
  571. }
  572. item.StartDate = info.StartDate;
  573. if (item.EndDate != info.EndDate)
  574. {
  575. forceUpdate = true;
  576. }
  577. item.EndDate = info.EndDate;
  578. item.ProductionYear = info.ProductionYear;
  579. if (!isSeries || info.IsRepeat)
  580. {
  581. item.PremiereDate = info.OriginalAirDate;
  582. }
  583. item.IndexNumber = info.EpisodeNumber;
  584. item.ParentIndexNumber = info.SeasonNumber;
  585. if (!item.HasImage(ImageType.Primary))
  586. {
  587. if (!string.IsNullOrWhiteSpace(info.ImagePath))
  588. {
  589. item.SetImage(new ItemImageInfo
  590. {
  591. Path = info.ImagePath,
  592. Type = ImageType.Primary
  593. }, 0);
  594. }
  595. else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
  596. {
  597. item.SetImage(new ItemImageInfo
  598. {
  599. Path = info.ImageUrl,
  600. Type = ImageType.Primary
  601. }, 0);
  602. }
  603. }
  604. if (!item.HasImage(ImageType.Thumb))
  605. {
  606. if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
  607. {
  608. item.SetImage(new ItemImageInfo
  609. {
  610. Path = info.ThumbImageUrl,
  611. Type = ImageType.Thumb
  612. }, 0);
  613. }
  614. }
  615. if (!item.HasImage(ImageType.Logo))
  616. {
  617. if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
  618. {
  619. item.SetImage(new ItemImageInfo
  620. {
  621. Path = info.LogoImageUrl,
  622. Type = ImageType.Logo
  623. }, 0);
  624. }
  625. }
  626. if (!item.HasImage(ImageType.Backdrop))
  627. {
  628. if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
  629. {
  630. item.SetImage(new ItemImageInfo
  631. {
  632. Path = info.BackdropImageUrl,
  633. Type = ImageType.Backdrop
  634. }, 0);
  635. }
  636. }
  637. var isUpdated = false;
  638. if (isNew)
  639. {
  640. }
  641. else if (forceUpdate || string.IsNullOrWhiteSpace(info.Etag))
  642. {
  643. isUpdated = true;
  644. }
  645. else
  646. {
  647. var etag = info.Etag;
  648. if (!string.Equals(etag, item.GetProviderId(EtagKey), StringComparison.OrdinalIgnoreCase))
  649. {
  650. item.SetProviderId(EtagKey, etag);
  651. isUpdated = true;
  652. }
  653. }
  654. if (isNew || isUpdated)
  655. {
  656. item.OnMetadataChanged();
  657. }
  658. return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
  659. }
  660. public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
  661. {
  662. var program = _libraryManager.GetItemById(id);
  663. var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
  664. var list = new List<Tuple<BaseItemDto, string, string>>();
  665. var externalSeriesId = program.ExternalSeriesId;
  666. list.Add(new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, externalSeriesId));
  667. await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
  668. return dto;
  669. }
  670. public async Task<QueryResult<BaseItemDto>> GetPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
  671. {
  672. var user = query.User;
  673. var topFolder = GetInternalLiveTvFolder(cancellationToken);
  674. if (query.OrderBy.Length == 0)
  675. {
  676. if (query.IsAiring ?? false)
  677. {
  678. // Unless something else was specified, order by start date to take advantage of a specialized index
  679. query.OrderBy = new ValueTuple<string, SortOrder>[]
  680. {
  681. new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending)
  682. };
  683. }
  684. else
  685. {
  686. // Unless something else was specified, order by start date to take advantage of a specialized index
  687. query.OrderBy = new ValueTuple<string, SortOrder>[]
  688. {
  689. new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending)
  690. };
  691. }
  692. }
  693. RemoveFields(options);
  694. var internalQuery = new InternalItemsQuery(user)
  695. {
  696. IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
  697. MinEndDate = query.MinEndDate,
  698. MinStartDate = query.MinStartDate,
  699. MaxEndDate = query.MaxEndDate,
  700. MaxStartDate = query.MaxStartDate,
  701. ChannelIds = query.ChannelIds,
  702. IsMovie = query.IsMovie,
  703. IsSeries = query.IsSeries,
  704. IsSports = query.IsSports,
  705. IsKids = query.IsKids,
  706. IsNews = query.IsNews,
  707. Genres = query.Genres,
  708. GenreIds = query.GenreIds,
  709. StartIndex = query.StartIndex,
  710. Limit = query.Limit,
  711. OrderBy = query.OrderBy,
  712. EnableTotalRecordCount = query.EnableTotalRecordCount,
  713. TopParentIds = new[] { topFolder.Id },
  714. Name = query.Name,
  715. DtoOptions = options,
  716. HasAired = query.HasAired,
  717. IsAiring = query.IsAiring
  718. };
  719. if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
  720. {
  721. var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
  722. var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
  723. if (seriesTimer != null)
  724. {
  725. internalQuery.ExternalSeriesId = seriesTimer.SeriesId;
  726. if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId))
  727. {
  728. // Better to return nothing than every program in the database
  729. return new QueryResult<BaseItemDto>();
  730. }
  731. }
  732. else
  733. {
  734. // Better to return nothing than every program in the database
  735. return new QueryResult<BaseItemDto>();
  736. }
  737. }
  738. var queryResult = _libraryManager.QueryItems(internalQuery);
  739. var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
  740. var result = new QueryResult<BaseItemDto>
  741. {
  742. Items = returnArray,
  743. TotalRecordCount = queryResult.TotalRecordCount
  744. };
  745. return result;
  746. }
  747. public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
  748. {
  749. var user = query.User;
  750. var topFolder = GetInternalLiveTvFolder(cancellationToken);
  751. var internalQuery = new InternalItemsQuery(user)
  752. {
  753. IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
  754. IsAiring = query.IsAiring,
  755. HasAired = query.HasAired,
  756. IsNews = query.IsNews,
  757. IsMovie = query.IsMovie,
  758. IsSeries = query.IsSeries,
  759. IsSports = query.IsSports,
  760. IsKids = query.IsKids,
  761. EnableTotalRecordCount = query.EnableTotalRecordCount,
  762. OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
  763. TopParentIds = new[] { topFolder.Id },
  764. DtoOptions = options,
  765. GenreIds = query.GenreIds
  766. };
  767. if (query.Limit.HasValue)
  768. {
  769. internalQuery.Limit = Math.Max(query.Limit.Value * 4, 200);
  770. }
  771. var programList = _libraryManager.QueryItems(internalQuery).Items;
  772. var totalCount = programList.Length;
  773. IOrderedEnumerable<LiveTvProgram> orderedPrograms = programList.Cast<LiveTvProgram>().OrderBy(i => i.StartDate.Date);
  774. if (query.IsAiring ?? false)
  775. {
  776. orderedPrograms = orderedPrograms
  777. .ThenByDescending(i => GetRecommendationScore(i, user, true));
  778. }
  779. IEnumerable<BaseItem> programs = orderedPrograms;
  780. if (query.Limit.HasValue)
  781. {
  782. programs = programs.Take(query.Limit.Value);
  783. }
  784. var result = new QueryResult<BaseItem>
  785. {
  786. Items = programs.ToArray(),
  787. TotalRecordCount = totalCount
  788. };
  789. return result;
  790. }
  791. public QueryResult<BaseItemDto> GetRecommendedPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
  792. {
  793. if (!(query.IsAiring ?? false))
  794. {
  795. return GetPrograms(query, options, cancellationToken).Result;
  796. }
  797. RemoveFields(options);
  798. var internalResult = GetRecommendedProgramsInternal(query, options, cancellationToken);
  799. var user = query.User;
  800. var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
  801. var result = new QueryResult<BaseItemDto>
  802. {
  803. Items = returnArray,
  804. TotalRecordCount = internalResult.TotalRecordCount
  805. };
  806. return result;
  807. }
  808. private int GetRecommendationScore(LiveTvProgram program, User user, bool factorChannelWatchCount)
  809. {
  810. var score = 0;
  811. if (program.IsLive)
  812. {
  813. score++;
  814. }
  815. if (program.IsSeries && !program.IsRepeat)
  816. {
  817. score++;
  818. }
  819. var channel = _libraryManager.GetItemById(program.ChannelId);
  820. if (channel != null)
  821. {
  822. var channelUserdata = _userDataManager.GetUserData(user, channel);
  823. if (channelUserdata.Likes ?? false)
  824. {
  825. score += 2;
  826. }
  827. else if (!(channelUserdata.Likes ?? true))
  828. {
  829. score -= 2;
  830. }
  831. if (channelUserdata.IsFavorite)
  832. {
  833. score += 3;
  834. }
  835. if (factorChannelWatchCount)
  836. {
  837. score += channelUserdata.PlayCount;
  838. }
  839. }
  840. return score;
  841. }
  842. private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string>> programs, CancellationToken cancellationToken)
  843. {
  844. var timers = new Dictionary<string, List<TimerInfo>>();
  845. var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>();
  846. TimerInfo[] timerList = null;
  847. SeriesTimerInfo[] seriesTimerList = null;
  848. foreach (var programTuple in programs)
  849. {
  850. var program = programTuple.Item1;
  851. var externalProgramId = programTuple.Item2;
  852. string externalSeriesId = programTuple.Item3;
  853. if (timerList == null)
  854. {
  855. timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
  856. }
  857. var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase));
  858. var foundSeriesTimer = false;
  859. if (timer != null)
  860. {
  861. if (timer.Status != RecordingStatus.Cancelled && timer.Status != RecordingStatus.Error)
  862. {
  863. program.TimerId = _tvDtoService.GetInternalTimerId(timer.Id);
  864. program.Status = timer.Status.ToString();
  865. }
  866. if (!string.IsNullOrEmpty(timer.SeriesTimerId))
  867. {
  868. program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.SeriesTimerId)
  869. .ToString("N");
  870. foundSeriesTimer = true;
  871. }
  872. }
  873. if (foundSeriesTimer || string.IsNullOrWhiteSpace(externalSeriesId))
  874. {
  875. continue;
  876. }
  877. if (seriesTimerList == null)
  878. {
  879. seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
  880. }
  881. var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase));
  882. if (seriesTimer != null)
  883. {
  884. program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(seriesTimer.Id)
  885. .ToString("N");
  886. }
  887. }
  888. }
  889. internal Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
  890. {
  891. return RefreshChannelsInternal(progress, cancellationToken);
  892. }
  893. private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
  894. {
  895. await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false);
  896. await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
  897. var numComplete = 0;
  898. double progressPerService = _services.Length == 0
  899. ? 0
  900. : 1 / _services.Length;
  901. var newChannelIdList = new List<Guid>();
  902. var newProgramIdList = new List<Guid>();
  903. var cleanDatabase = true;
  904. foreach (var service in _services)
  905. {
  906. cancellationToken.ThrowIfCancellationRequested();
  907. _logger.LogDebug("Refreshing guide from {name}", service.Name);
  908. try
  909. {
  910. var innerProgress = new ActionableProgress<double>();
  911. innerProgress.RegisterAction(p => progress.Report(p * progressPerService));
  912. var idList = await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
  913. newChannelIdList.AddRange(idList.Item1);
  914. newProgramIdList.AddRange(idList.Item2);
  915. }
  916. catch (OperationCanceledException)
  917. {
  918. throw;
  919. }
  920. catch (Exception ex)
  921. {
  922. cleanDatabase = false;
  923. _logger.LogError(ex, "Error refreshing channels for service");
  924. }
  925. numComplete++;
  926. double percent = numComplete;
  927. percent /= _services.Length;
  928. progress.Report(100 * percent);
  929. }
  930. if (cleanDatabase)
  931. {
  932. CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken);
  933. CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken);
  934. }
  935. var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
  936. if (coreService != null)
  937. {
  938. await coreService.RefreshSeriesTimers(cancellationToken, new SimpleProgress<double>()).ConfigureAwait(false);
  939. await coreService.RefreshTimers(cancellationToken, new SimpleProgress<double>()).ConfigureAwait(false);
  940. }
  941. // Load these now which will prefetch metadata
  942. var dtoOptions = new DtoOptions();
  943. var fields = dtoOptions.Fields.ToList();
  944. fields.Remove(ItemFields.BasicSyncInfo);
  945. dtoOptions.Fields = fields.ToArray();
  946. progress.Report(100);
  947. }
  948. private async Task<Tuple<List<Guid>, List<Guid>>> RefreshChannelsInternal(ILiveTvService service, IProgress<double> progress, CancellationToken cancellationToken)
  949. {
  950. progress.Report(10);
  951. var allChannelsList = (await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false))
  952. .Select(i => new Tuple<string, ChannelInfo>(service.Name, i))
  953. .ToList();
  954. var list = new List<LiveTvChannel>();
  955. var numComplete = 0;
  956. var parentFolder = GetInternalLiveTvFolder(cancellationToken);
  957. var parentFolderId = parentFolder.Id;
  958. foreach (var channelInfo in allChannelsList)
  959. {
  960. cancellationToken.ThrowIfCancellationRequested();
  961. try
  962. {
  963. var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
  964. list.Add(item);
  965. }
  966. catch (OperationCanceledException)
  967. {
  968. throw;
  969. }
  970. catch (Exception ex)
  971. {
  972. _logger.LogError(ex, "Error getting channel information for {name}", channelInfo.Item2.Name);
  973. }
  974. numComplete++;
  975. double percent = numComplete;
  976. percent /= allChannelsList.Count;
  977. progress.Report(5 * percent + 10);
  978. }
  979. progress.Report(15);
  980. numComplete = 0;
  981. var programs = new List<Guid>();
  982. var channels = new List<Guid>();
  983. var guideDays = GetGuideDays();
  984. _logger.LogInformation("Refreshing guide with {0} days of guide data", guideDays);
  985. cancellationToken.ThrowIfCancellationRequested();
  986. foreach (var currentChannel in list)
  987. {
  988. channels.Add(currentChannel.Id);
  989. cancellationToken.ThrowIfCancellationRequested();
  990. try
  991. {
  992. var start = DateTime.UtcNow.AddHours(-1);
  993. var end = start.AddDays(guideDays);
  994. var isMovie = false;
  995. var isSports = false;
  996. var isNews = false;
  997. var isKids = false;
  998. var iSSeries = false;
  999. var channelPrograms = (await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false)).ToList();
  1000. var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
  1001. {
  1002. IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
  1003. ChannelIds = new Guid[] { currentChannel.Id },
  1004. DtoOptions = new DtoOptions(true)
  1005. }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
  1006. var newPrograms = new List<LiveTvProgram>();
  1007. var updatedPrograms = new List<BaseItem>();
  1008. foreach (var program in channelPrograms)
  1009. {
  1010. var programTuple = GetProgram(program, existingPrograms, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken);
  1011. var programItem = programTuple.Item1;
  1012. if (programTuple.Item2)
  1013. {
  1014. newPrograms.Add(programItem);
  1015. }
  1016. else if (programTuple.Item3)
  1017. {
  1018. updatedPrograms.Add(programItem);
  1019. }
  1020. programs.Add(programItem.Id);
  1021. if (program.IsMovie)
  1022. {
  1023. isMovie = true;
  1024. }
  1025. if (program.IsSeries)
  1026. {
  1027. iSSeries = true;
  1028. }
  1029. if (program.IsSports)
  1030. {
  1031. isSports = true;
  1032. }
  1033. if (program.IsNews)
  1034. {
  1035. isNews = true;
  1036. }
  1037. if (program.IsKids)
  1038. {
  1039. isKids = true;
  1040. }
  1041. }
  1042. _logger.LogDebug("Channel {0} has {1} new programs and {2} updated programs", currentChannel.Name, newPrograms.Count, updatedPrograms.Count);
  1043. if (newPrograms.Count > 0)
  1044. {
  1045. _libraryManager.CreateItems(newPrograms, null, cancellationToken);
  1046. }
  1047. if (updatedPrograms.Count > 0)
  1048. {
  1049. _libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
  1050. }
  1051. currentChannel.IsMovie = isMovie;
  1052. currentChannel.IsNews = isNews;
  1053. currentChannel.IsSports = isSports;
  1054. currentChannel.IsSeries = iSSeries;
  1055. if (isKids)
  1056. {
  1057. currentChannel.AddTag("Kids");
  1058. }
  1059. //currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
  1060. await currentChannel.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
  1061. {
  1062. ForceSave = true
  1063. }, cancellationToken).ConfigureAwait(false);
  1064. }
  1065. catch (OperationCanceledException)
  1066. {
  1067. throw;
  1068. }
  1069. catch (Exception ex)
  1070. {
  1071. _logger.LogError(ex, "Error getting programs for channel {name}", currentChannel.Name);
  1072. }
  1073. numComplete++;
  1074. double percent = numComplete;
  1075. percent /= allChannelsList.Count;
  1076. progress.Report(85 * percent + 15);
  1077. }
  1078. progress.Report(100);
  1079. return new Tuple<List<Guid>, List<Guid>>(channels, programs);
  1080. }
  1081. private void CleanDatabaseInternal(Guid[] currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
  1082. {
  1083. var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
  1084. {
  1085. IncludeItemTypes = validTypes,
  1086. DtoOptions = new DtoOptions(false)
  1087. });
  1088. var numComplete = 0;
  1089. foreach (var itemId in list)
  1090. {
  1091. cancellationToken.ThrowIfCancellationRequested();
  1092. if (itemId.Equals(Guid.Empty))
  1093. {
  1094. // Somehow some invalid data got into the db. It probably predates the boundary checking
  1095. continue;
  1096. }
  1097. if (!currentIdList.Contains(itemId))
  1098. {
  1099. var item = _libraryManager.GetItemById(itemId);
  1100. if (item != null)
  1101. {
  1102. _libraryManager.DeleteItem(item, new DeleteOptions
  1103. {
  1104. DeleteFileLocation = false,
  1105. DeleteFromExternalProvider = false
  1106. }, false);
  1107. }
  1108. }
  1109. numComplete++;
  1110. double percent = numComplete;
  1111. percent /= list.Count;
  1112. progress.Report(100 * percent);
  1113. }
  1114. }
  1115. private const int MaxGuideDays = 14;
  1116. private double GetGuideDays()
  1117. {
  1118. var config = GetConfiguration();
  1119. if (config.GuideDays.HasValue)
  1120. {
  1121. return Math.Max(1, Math.Min(config.GuideDays.Value, MaxGuideDays));
  1122. }
  1123. return 7;
  1124. }
  1125. private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
  1126. {
  1127. if (user == null)
  1128. {
  1129. return new QueryResult<BaseItem>();
  1130. }
  1131. var folderIds = GetRecordingFolders(user, true)
  1132. .Select(i => i.Id)
  1133. .ToList();
  1134. var excludeItemTypes = new List<string>();
  1135. if (folderIds.Count == 0)
  1136. {
  1137. return new QueryResult<BaseItem>();
  1138. }
  1139. var includeItemTypes = new List<string>();
  1140. var genres = new List<string>();
  1141. if (query.IsMovie.HasValue)
  1142. {
  1143. if (query.IsMovie.Value)
  1144. {
  1145. includeItemTypes.Add(typeof(Movie).Name);
  1146. }
  1147. else
  1148. {
  1149. excludeItemTypes.Add(typeof(Movie).Name);
  1150. }
  1151. }
  1152. if (query.IsSeries.HasValue)
  1153. {
  1154. if (query.IsSeries.Value)
  1155. {
  1156. includeItemTypes.Add(typeof(Episode).Name);
  1157. }
  1158. else
  1159. {
  1160. excludeItemTypes.Add(typeof(Episode).Name);
  1161. }
  1162. }
  1163. if (query.IsSports.HasValue)
  1164. {
  1165. if (query.IsSports.Value)
  1166. {
  1167. genres.Add("Sports");
  1168. }
  1169. }
  1170. if (query.IsKids.HasValue)
  1171. {
  1172. if (query.IsKids.Value)
  1173. {
  1174. genres.Add("Kids");
  1175. genres.Add("Children");
  1176. genres.Add("Family");
  1177. }
  1178. }
  1179. var limit = query.Limit;
  1180. if ((query.IsInProgress ?? false))
  1181. {
  1182. limit = (query.Limit ?? 10) * 2;
  1183. limit = null;
  1184. //var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
  1185. //var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
  1186. //return new QueryResult<BaseItem>
  1187. //{
  1188. // Items = items,
  1189. // TotalRecordCount = items.Length
  1190. //};
  1191. dtoOptions.Fields = dtoOptions.Fields.Concat(new[] { ItemFields.Tags }).Distinct().ToArray();
  1192. }
  1193. var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
  1194. {
  1195. MediaTypes = new[] { MediaType.Video },
  1196. Recursive = true,
  1197. AncestorIds = folderIds.ToArray(),
  1198. IsFolder = false,
  1199. IsVirtualItem = false,
  1200. Limit = limit,
  1201. StartIndex = query.StartIndex,
  1202. OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DateCreated, SortOrder.Descending) },
  1203. EnableTotalRecordCount = query.EnableTotalRecordCount,
  1204. IncludeItemTypes = includeItemTypes.ToArray(),
  1205. ExcludeItemTypes = excludeItemTypes.ToArray(),
  1206. Genres = genres.ToArray(),
  1207. DtoOptions = dtoOptions
  1208. });
  1209. if ((query.IsInProgress ?? false))
  1210. {
  1211. result.Items = result
  1212. .Items
  1213. .OfType<Video>()
  1214. .Where(i => !i.IsCompleteMedia)
  1215. .ToArray();
  1216. result.TotalRecordCount = result.Items.Length;
  1217. }
  1218. return result;
  1219. }
  1220. public Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, ItemFields[] fields, User user = null)
  1221. {
  1222. var programTuples = new List<Tuple<BaseItemDto, string, string>>();
  1223. var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
  1224. var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo);
  1225. foreach (var tuple in tuples)
  1226. {
  1227. var program = (LiveTvProgram)tuple.Item1;
  1228. var dto = tuple.Item2;
  1229. dto.StartDate = program.StartDate;
  1230. dto.EpisodeTitle = program.EpisodeTitle;
  1231. if (program.IsRepeat)
  1232. {
  1233. dto.IsRepeat = program.IsRepeat;
  1234. }
  1235. if (program.IsMovie)
  1236. {
  1237. dto.IsMovie = program.IsMovie;
  1238. }
  1239. if (program.IsSeries)
  1240. {
  1241. dto.IsSeries = program.IsSeries;
  1242. }
  1243. if (program.IsSports)
  1244. {
  1245. dto.IsSports = program.IsSports;
  1246. }
  1247. if (program.IsLive)
  1248. {
  1249. dto.IsLive = program.IsLive;
  1250. }
  1251. if (program.IsNews)
  1252. {
  1253. dto.IsNews = program.IsNews;
  1254. }
  1255. if (program.IsKids)
  1256. {
  1257. dto.IsKids = program.IsKids;
  1258. }
  1259. if (program.IsPremiere)
  1260. {
  1261. dto.IsPremiere = program.IsPremiere;
  1262. }
  1263. if (hasChannelInfo || hasChannelImage)
  1264. {
  1265. var channel = _libraryManager.GetItemById(program.ChannelId) as LiveTvChannel;
  1266. if (channel != null)
  1267. {
  1268. dto.ChannelName = channel.Name;
  1269. dto.MediaType = channel.MediaType;
  1270. dto.ChannelNumber = channel.Number;
  1271. if (hasChannelImage && channel.HasImage(ImageType.Primary))
  1272. {
  1273. dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
  1274. }
  1275. }
  1276. }
  1277. var externalSeriesId = program.ExternalSeriesId;
  1278. programTuples.Add(new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, externalSeriesId));
  1279. }
  1280. return AddRecordingInfo(programTuples, CancellationToken.None);
  1281. }
  1282. public ActiveRecordingInfo GetActiveRecordingInfo(string path)
  1283. {
  1284. return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
  1285. }
  1286. public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
  1287. {
  1288. var service = EmbyTV.EmbyTV.Current;
  1289. var info = activeRecordingInfo.Timer;
  1290. var channel = string.IsNullOrWhiteSpace(info.ChannelId) ? null : _libraryManager.GetItemById(_tvDtoService.GetInternalChannelId(service.Name, info.ChannelId));
  1291. dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
  1292. ? null
  1293. : _tvDtoService.GetInternalSeriesTimerId(info.SeriesTimerId).ToString("N");
  1294. dto.TimerId = string.IsNullOrEmpty(info.Id)
  1295. ? null
  1296. : _tvDtoService.GetInternalTimerId(info.Id);
  1297. var startDate = info.StartDate;
  1298. var endDate = info.EndDate;
  1299. dto.StartDate = startDate;
  1300. dto.EndDate = endDate;
  1301. dto.Status = info.Status.ToString();
  1302. dto.IsRepeat = info.IsRepeat;
  1303. dto.EpisodeTitle = info.EpisodeTitle;
  1304. dto.IsMovie = info.IsMovie;
  1305. dto.IsSeries = info.IsSeries;
  1306. dto.IsSports = info.IsSports;
  1307. dto.IsLive = info.IsLive;
  1308. dto.IsNews = info.IsNews;
  1309. dto.IsKids = info.IsKids;
  1310. dto.IsPremiere = info.IsPremiere;
  1311. if (info.Status == RecordingStatus.InProgress)
  1312. {
  1313. startDate = info.StartDate.AddSeconds(0 - info.PrePaddingSeconds);
  1314. endDate = info.EndDate.AddSeconds(info.PostPaddingSeconds);
  1315. var now = DateTime.UtcNow.Ticks;
  1316. var start = startDate.Ticks;
  1317. var end = endDate.Ticks;
  1318. var pct = now - start;
  1319. pct /= end;
  1320. pct *= 100;
  1321. dto.CompletionPercentage = pct;
  1322. }
  1323. if (channel != null)
  1324. {
  1325. dto.ChannelName = channel.Name;
  1326. if (channel.HasImage(ImageType.Primary))
  1327. {
  1328. dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
  1329. }
  1330. }
  1331. }
  1332. public QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options)
  1333. {
  1334. var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
  1335. RemoveFields(options);
  1336. var internalResult = GetEmbyRecordings(query, options, user);
  1337. var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
  1338. return new QueryResult<BaseItemDto>
  1339. {
  1340. Items = returnArray,
  1341. TotalRecordCount = internalResult.TotalRecordCount
  1342. };
  1343. }
  1344. private async Task<QueryResult<TimerInfo>> GetTimersInternal(TimerQuery query, CancellationToken cancellationToken)
  1345. {
  1346. var tasks = _services.Select(async i =>
  1347. {
  1348. try
  1349. {
  1350. var recs = await i.GetTimersAsync(cancellationToken).ConfigureAwait(false);
  1351. return recs.Select(r => new Tuple<TimerInfo, ILiveTvService>(r, i));
  1352. }
  1353. catch (Exception ex)
  1354. {
  1355. _logger.LogError(ex, "Error getting recordings");
  1356. return new List<Tuple<TimerInfo, ILiveTvService>>();
  1357. }
  1358. });
  1359. var results = await Task.WhenAll(tasks).ConfigureAwait(false);
  1360. var timers = results.SelectMany(i => i.ToList());
  1361. if (query.IsActive.HasValue)
  1362. {
  1363. if (query.IsActive.Value)
  1364. {
  1365. timers = timers.Where(i => i.Item1.Status == RecordingStatus.InProgress);
  1366. }
  1367. else
  1368. {
  1369. timers = timers.Where(i => i.Item1.Status != RecordingStatus.InProgress);
  1370. }
  1371. }
  1372. if (query.IsScheduled.HasValue)
  1373. {
  1374. if (query.IsScheduled.Value)
  1375. {
  1376. timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
  1377. }
  1378. else
  1379. {
  1380. timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
  1381. }
  1382. }
  1383. if (!string.IsNullOrEmpty(query.ChannelId))
  1384. {
  1385. var guid = new Guid(query.ChannelId);
  1386. timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
  1387. }
  1388. if (!string.IsNullOrEmpty(query.SeriesTimerId))
  1389. {
  1390. var guid = new Guid(query.SeriesTimerId);
  1391. timers = timers
  1392. .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
  1393. }
  1394. if (!string.IsNullOrEmpty(query.Id))
  1395. {
  1396. var guid = new Guid(query.Id);
  1397. timers = timers
  1398. .Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
  1399. }
  1400. var returnArray = timers
  1401. .Select(i => i.Item1)
  1402. .OrderBy(i => i.StartDate)
  1403. .ToArray();
  1404. return new QueryResult<TimerInfo>
  1405. {
  1406. Items = returnArray,
  1407. TotalRecordCount = returnArray.Length
  1408. };
  1409. }
  1410. public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
  1411. {
  1412. var tasks = _services.Select(async i =>
  1413. {
  1414. try
  1415. {
  1416. var recs = await i.GetTimersAsync(cancellationToken).ConfigureAwait(false);
  1417. return recs.Select(r => new Tuple<TimerInfo, ILiveTvService>(r, i));
  1418. }
  1419. catch (Exception ex)
  1420. {
  1421. _logger.LogError(ex, "Error getting recordings");
  1422. return new List<Tuple<TimerInfo, ILiveTvService>>();
  1423. }
  1424. });
  1425. var results = await Task.WhenAll(tasks).ConfigureAwait(false);
  1426. var timers = results.SelectMany(i => i.ToList());
  1427. if (query.IsActive.HasValue)
  1428. {
  1429. if (query.IsActive.Value)
  1430. {
  1431. timers = timers.Where(i => i.Item1.Status == RecordingStatus.InProgress);
  1432. }
  1433. else
  1434. {
  1435. timers = timers.Where(i => i.Item1.Status != RecordingStatus.InProgress);
  1436. }
  1437. }
  1438. if (query.IsScheduled.HasValue)
  1439. {
  1440. if (query.IsScheduled.Value)
  1441. {
  1442. timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
  1443. }
  1444. else
  1445. {
  1446. timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
  1447. }
  1448. }
  1449. if (!string.IsNullOrEmpty(query.ChannelId))
  1450. {
  1451. var guid = new Guid(query.ChannelId);
  1452. timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
  1453. }
  1454. if (!string.IsNullOrEmpty(query.SeriesTimerId))
  1455. {
  1456. var guid = new Guid(query.SeriesTimerId);
  1457. timers = timers
  1458. .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
  1459. }
  1460. if (!string.IsNullOrEmpty(query.Id))
  1461. {
  1462. timers = timers
  1463. .Where(i => string.Equals(_tvDtoService.GetInternalTimerId(i.Item1.Id), query.Id, StringComparison.OrdinalIgnoreCase));
  1464. }
  1465. var returnList = new List<TimerInfoDto>();
  1466. foreach (var i in timers)
  1467. {
  1468. var program = string.IsNullOrEmpty(i.Item1.ProgramId) ?
  1469. null :
  1470. _libraryManager.GetItemById(_tvDtoService.GetInternalProgramId(i.Item1.ProgramId)) as LiveTvProgram;
  1471. var channel = string.IsNullOrEmpty(i.Item1.ChannelId) ? null : _libraryManager.GetItemById(_tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
  1472. returnList.Add(_tvDtoService.GetTimerInfoDto(i.Item1, i.Item2, program, channel));
  1473. }
  1474. var returnArray = returnList
  1475. .OrderBy(i => i.StartDate)
  1476. .ToArray();
  1477. return new QueryResult<TimerInfoDto>
  1478. {
  1479. Items = returnArray,
  1480. TotalRecordCount = returnArray.Length
  1481. };
  1482. }
  1483. public async Task CancelTimer(string id)
  1484. {
  1485. var timer = await GetTimer(id, CancellationToken.None).ConfigureAwait(false);
  1486. if (timer == null)
  1487. {
  1488. throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
  1489. }
  1490. var service = GetService(timer.ServiceName);
  1491. await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
  1492. if (!(service is EmbyTV.EmbyTV))
  1493. {
  1494. EventHelper.FireEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
  1495. {
  1496. Argument = new TimerEventInfo
  1497. {
  1498. Id = id
  1499. }
  1500. }, _logger);
  1501. }
  1502. }
  1503. public async Task CancelSeriesTimer(string id)
  1504. {
  1505. var timer = await GetSeriesTimer(id, CancellationToken.None).ConfigureAwait(false);
  1506. if (timer == null)
  1507. {
  1508. throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
  1509. }
  1510. var service = GetService(timer.ServiceName);
  1511. await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
  1512. EventHelper.FireEventIfNotNull(SeriesTimerCancelled, this, new GenericEventArgs<TimerEventInfo>
  1513. {
  1514. Argument = new TimerEventInfo
  1515. {
  1516. Id = id
  1517. }
  1518. }, _logger);
  1519. }
  1520. public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
  1521. {
  1522. var results = await GetTimers(new TimerQuery
  1523. {
  1524. Id = id
  1525. }, cancellationToken).ConfigureAwait(false);
  1526. return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
  1527. }
  1528. public async Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken)
  1529. {
  1530. var results = await GetSeriesTimers(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
  1531. return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
  1532. }
  1533. private async Task<QueryResult<SeriesTimerInfo>> GetSeriesTimersInternal(SeriesTimerQuery query, CancellationToken cancellationToken)
  1534. {
  1535. var tasks = _services.Select(async i =>
  1536. {
  1537. try
  1538. {
  1539. var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
  1540. return recs.Select(r =>
  1541. {
  1542. r.ServiceName = i.Name;
  1543. return new Tuple<SeriesTimerInfo, ILiveTvService>(r, i);
  1544. });
  1545. }
  1546. catch (Exception ex)
  1547. {
  1548. _logger.LogError(ex, "Error getting recordings");
  1549. return new List<Tuple<SeriesTimerInfo, ILiveTvService>>();
  1550. }
  1551. });
  1552. var results = await Task.WhenAll(tasks).ConfigureAwait(false);
  1553. var timers = results.SelectMany(i => i.ToList());
  1554. if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase))
  1555. {
  1556. timers = query.SortOrder == SortOrder.Descending ?
  1557. timers.OrderBy(i => i.Item1.Priority).ThenByStringDescending(i => i.Item1.Name) :
  1558. timers.OrderByDescending(i => i.Item1.Priority).ThenByString(i => i.Item1.Name);
  1559. }
  1560. else
  1561. {
  1562. timers = query.SortOrder == SortOrder.Descending ?
  1563. timers.OrderByStringDescending(i => i.Item1.Name) :
  1564. timers.OrderByString(i => i.Item1.Name);
  1565. }
  1566. var returnArray = timers
  1567. .Select(i =>
  1568. {
  1569. return i.Item1;
  1570. })
  1571. .ToArray();
  1572. return new QueryResult<SeriesTimerInfo>
  1573. {
  1574. Items = returnArray,
  1575. TotalRecordCount = returnArray.Length
  1576. };
  1577. }
  1578. public async Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken)
  1579. {
  1580. var tasks = _services.Select(async i =>
  1581. {
  1582. try
  1583. {
  1584. var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
  1585. return recs.Select(r => new Tuple<SeriesTimerInfo, ILiveTvService>(r, i));
  1586. }
  1587. catch (Exception ex)
  1588. {
  1589. _logger.LogError(ex, "Error getting recordings");
  1590. return new List<Tuple<SeriesTimerInfo, ILiveTvService>>();
  1591. }
  1592. });
  1593. var results = await Task.WhenAll(tasks).ConfigureAwait(false);
  1594. var timers = results.SelectMany(i => i.ToList());
  1595. if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase))
  1596. {
  1597. timers = query.SortOrder == SortOrder.Descending ?
  1598. timers.OrderBy(i => i.Item1.Priority).ThenByStringDescending(i => i.Item1.Name) :
  1599. timers.OrderByDescending(i => i.Item1.Priority).ThenByString(i => i.Item1.Name);
  1600. }
  1601. else
  1602. {
  1603. timers = query.SortOrder == SortOrder.Descending ?
  1604. timers.OrderByStringDescending(i => i.Item1.Name) :
  1605. timers.OrderByString(i => i.Item1.Name);
  1606. }
  1607. var returnArray = timers
  1608. .Select(i =>
  1609. {
  1610. string channelName = null;
  1611. if (!string.IsNullOrEmpty(i.Item1.ChannelId))
  1612. {
  1613. var internalChannelId = _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId);
  1614. var channel = _libraryManager.GetItemById(internalChannelId);
  1615. channelName = channel == null ? null : channel.Name;
  1616. }
  1617. return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
  1618. })
  1619. .ToArray();
  1620. return new QueryResult<SeriesTimerInfoDto>
  1621. {
  1622. Items = returnArray,
  1623. TotalRecordCount = returnArray.Length
  1624. };
  1625. }
  1626. public BaseItem GetLiveTvChannel(TimerInfo timer, ILiveTvService service)
  1627. {
  1628. var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, timer.ChannelId);
  1629. return _libraryManager.GetItemById(internalChannelId);
  1630. }
  1631. public void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> tuples, DtoOptions options, User user)
  1632. {
  1633. var now = DateTime.UtcNow;
  1634. var channelIds = tuples.Select(i => i.Item2.Id).Distinct().ToArray();
  1635. var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
  1636. {
  1637. IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
  1638. ChannelIds = channelIds,
  1639. MaxStartDate = now,
  1640. MinEndDate = now,
  1641. Limit = channelIds.Length,
  1642. OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
  1643. TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
  1644. DtoOptions = options
  1645. }) : new List<BaseItem>();
  1646. RemoveFields(options);
  1647. var currentProgramsList = new List<BaseItem>();
  1648. var currentChannelsDict = new Dictionary<Guid, BaseItemDto>();
  1649. var addCurrentProgram = options.AddCurrentProgram;
  1650. foreach (var tuple in tuples)
  1651. {
  1652. var dto = tuple.Item1;
  1653. var channel = tuple.Item2;
  1654. dto.Number = channel.Number;
  1655. dto.ChannelNumber = channel.Number;
  1656. dto.ChannelType = channel.ChannelType;
  1657. currentChannelsDict[dto.Id] = dto;
  1658. if (addCurrentProgram)
  1659. {
  1660. var currentProgram = programs.FirstOrDefault(i => channel.Id.Equals(i.ChannelId));
  1661. if (currentProgram != null)
  1662. {
  1663. currentProgramsList.Add(currentProgram);
  1664. }
  1665. }
  1666. }
  1667. if (addCurrentProgram)
  1668. {
  1669. var currentProgramDtos = _dtoService.GetBaseItemDtos(currentProgramsList, options, user);
  1670. foreach (var programDto in currentProgramDtos)
  1671. {
  1672. BaseItemDto channelDto;
  1673. if (currentChannelsDict.TryGetValue(programDto.ChannelId, out channelDto))
  1674. {
  1675. channelDto.CurrentProgram = programDto;
  1676. }
  1677. }
  1678. }
  1679. }
  1680. private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
  1681. {
  1682. var service = program != null ?
  1683. GetService(program) :
  1684. null;
  1685. if (service == null)
  1686. {
  1687. service = _services.First();
  1688. }
  1689. ProgramInfo programInfo = null;
  1690. if (program != null)
  1691. {
  1692. var channel = _libraryManager.GetItemById(program.ChannelId);
  1693. programInfo = new ProgramInfo
  1694. {
  1695. Audio = program.Audio,
  1696. ChannelId = channel.ExternalId,
  1697. CommunityRating = program.CommunityRating,
  1698. EndDate = program.EndDate ?? DateTime.MinValue,
  1699. EpisodeTitle = program.EpisodeTitle,
  1700. Genres = program.Genres.ToList(),
  1701. Id = program.ExternalId,
  1702. IsHD = program.IsHD,
  1703. IsKids = program.IsKids,
  1704. IsLive = program.IsLive,
  1705. IsMovie = program.IsMovie,
  1706. IsNews = program.IsNews,
  1707. IsPremiere = program.IsPremiere,
  1708. IsRepeat = program.IsRepeat,
  1709. IsSeries = program.IsSeries,
  1710. IsSports = program.IsSports,
  1711. OriginalAirDate = program.PremiereDate,
  1712. Overview = program.Overview,
  1713. StartDate = program.StartDate,
  1714. //ImagePath = program.ExternalImagePath,
  1715. Name = program.Name,
  1716. OfficialRating = program.OfficialRating
  1717. };
  1718. }
  1719. var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
  1720. info.RecordAnyTime = true;
  1721. info.Days = new List<DayOfWeek>
  1722. {
  1723. DayOfWeek.Sunday,
  1724. DayOfWeek.Monday,
  1725. DayOfWeek.Tuesday,
  1726. DayOfWeek.Wednesday,
  1727. DayOfWeek.Thursday,
  1728. DayOfWeek.Friday,
  1729. DayOfWeek.Saturday
  1730. };
  1731. info.Id = null;
  1732. return new Tuple<SeriesTimerInfo, ILiveTvService>(info, service);
  1733. }
  1734. public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(CancellationToken cancellationToken)
  1735. {
  1736. var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
  1737. var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
  1738. return obj;
  1739. }
  1740. public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
  1741. {
  1742. var program = (LiveTvProgram)_libraryManager.GetItemById(programId);
  1743. var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false);
  1744. var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
  1745. var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null);
  1746. info.Days = defaults.Item1.Days.ToArray();
  1747. info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
  1748. info.Name = program.Name;
  1749. info.ChannelId = programDto.ChannelId;
  1750. info.ChannelName = programDto.ChannelName;
  1751. info.StartDate = program.StartDate;
  1752. info.Name = program.Name;
  1753. info.Overview = program.Overview;
  1754. info.ProgramId = programDto.Id.ToString("N");
  1755. info.ExternalProgramId = program.ExternalId;
  1756. if (program.EndDate.HasValue)
  1757. {
  1758. info.EndDate = program.EndDate.Value;
  1759. }
  1760. return info;
  1761. }
  1762. public async Task CreateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
  1763. {
  1764. var service = GetService(timer.ServiceName);
  1765. var info = await _tvDtoService.GetTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
  1766. // Set priority from default values
  1767. var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
  1768. info.Priority = defaultValues.Priority;
  1769. string newTimerId = null;
  1770. var supportsNewTimerIds = service as ISupportsNewTimerIds;
  1771. if (supportsNewTimerIds != null)
  1772. {
  1773. newTimerId = await supportsNewTimerIds.CreateTimer(info, cancellationToken).ConfigureAwait(false);
  1774. newTimerId = _tvDtoService.GetInternalTimerId(newTimerId);
  1775. }
  1776. else
  1777. {
  1778. await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1779. }
  1780. _logger.LogInformation("New recording scheduled");
  1781. if (!(service is EmbyTV.EmbyTV))
  1782. {
  1783. EventHelper.FireEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
  1784. {
  1785. Argument = new TimerEventInfo
  1786. {
  1787. ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId),
  1788. Id = newTimerId
  1789. }
  1790. }, _logger);
  1791. }
  1792. }
  1793. public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
  1794. {
  1795. var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
  1796. if (!registration.IsValid)
  1797. {
  1798. _logger.LogInformation("Creating series recordings requires an active Emby Premiere subscription.");
  1799. return;
  1800. }
  1801. var service = GetService(timer.ServiceName);
  1802. var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
  1803. // Set priority from default values
  1804. var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
  1805. info.Priority = defaultValues.Priority;
  1806. string newTimerId = null;
  1807. var supportsNewTimerIds = service as ISupportsNewTimerIds;
  1808. if (supportsNewTimerIds != null)
  1809. {
  1810. newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
  1811. newTimerId = _tvDtoService.GetInternalSeriesTimerId(newTimerId).ToString("N");
  1812. }
  1813. else
  1814. {
  1815. await service.CreateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1816. }
  1817. EventHelper.FireEventIfNotNull(SeriesTimerCreated, this, new GenericEventArgs<TimerEventInfo>
  1818. {
  1819. Argument = new TimerEventInfo
  1820. {
  1821. ProgramId = _tvDtoService.GetInternalProgramId(info.ProgramId),
  1822. Id = newTimerId
  1823. }
  1824. }, _logger);
  1825. }
  1826. public async Task UpdateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
  1827. {
  1828. var info = await _tvDtoService.GetTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
  1829. var service = GetService(timer.ServiceName);
  1830. await service.UpdateTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1831. }
  1832. public async Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
  1833. {
  1834. var info = await _tvDtoService.GetSeriesTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
  1835. var service = GetService(timer.ServiceName);
  1836. await service.UpdateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1837. }
  1838. public GuideInfo GetGuideInfo()
  1839. {
  1840. var startDate = DateTime.UtcNow;
  1841. var endDate = startDate.AddDays(GetGuideDays());
  1842. return new GuideInfo
  1843. {
  1844. StartDate = startDate,
  1845. EndDate = endDate
  1846. };
  1847. }
  1848. /// <summary>
  1849. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  1850. /// </summary>
  1851. public void Dispose()
  1852. {
  1853. Dispose(true);
  1854. }
  1855. private bool _isDisposed = false;
  1856. /// <summary>
  1857. /// Releases unmanaged and - optionally - managed resources.
  1858. /// </summary>
  1859. /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  1860. protected virtual void Dispose(bool dispose)
  1861. {
  1862. if (dispose)
  1863. {
  1864. _isDisposed = true;
  1865. }
  1866. }
  1867. private LiveTvServiceInfo[] GetServiceInfos()
  1868. {
  1869. return Services.Select(GetServiceInfo).ToArray();
  1870. }
  1871. private LiveTvServiceInfo GetServiceInfo(ILiveTvService service)
  1872. {
  1873. return new LiveTvServiceInfo
  1874. {
  1875. Name = service.Name
  1876. };
  1877. }
  1878. public LiveTvInfo GetLiveTvInfo(CancellationToken cancellationToken)
  1879. {
  1880. var services = GetServiceInfos();
  1881. var info = new LiveTvInfo
  1882. {
  1883. Services = services,
  1884. IsEnabled = services.Length > 0
  1885. };
  1886. info.EnabledUsers = _userManager.Users
  1887. .Where(IsLiveTvEnabled)
  1888. .Select(i => i.Id.ToString("N"))
  1889. .ToArray();
  1890. return info;
  1891. }
  1892. private bool IsLiveTvEnabled(User user)
  1893. {
  1894. return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
  1895. }
  1896. public IEnumerable<User> GetEnabledUsers()
  1897. {
  1898. return _userManager.Users
  1899. .Where(IsLiveTvEnabled);
  1900. }
  1901. /// <summary>
  1902. /// Resets the tuner.
  1903. /// </summary>
  1904. /// <param name="id">The identifier.</param>
  1905. /// <param name="cancellationToken">The cancellation token.</param>
  1906. /// <returns>Task.</returns>
  1907. public Task ResetTuner(string id, CancellationToken cancellationToken)
  1908. {
  1909. var parts = id.Split(new[] { '_' }, 2);
  1910. var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase));
  1911. if (service == null)
  1912. {
  1913. throw new ArgumentException("Service not found.");
  1914. }
  1915. return service.ResetTuner(parts[1], cancellationToken);
  1916. }
  1917. private void RemoveFields(DtoOptions options)
  1918. {
  1919. var fields = options.Fields.ToList();
  1920. fields.Remove(ItemFields.CanDelete);
  1921. fields.Remove(ItemFields.CanDownload);
  1922. fields.Remove(ItemFields.DisplayPreferencesId);
  1923. fields.Remove(ItemFields.Etag);
  1924. options.Fields = fields.ToArray();
  1925. }
  1926. public Folder GetInternalLiveTvFolder(CancellationToken cancellationToken)
  1927. {
  1928. var name = _localization.GetLocalizedString("HeaderLiveTV");
  1929. return _libraryManager.GetNamedView(name, CollectionType.LiveTv, name);
  1930. }
  1931. public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
  1932. {
  1933. info = _jsonSerializer.DeserializeFromString<TunerHostInfo>(_jsonSerializer.SerializeToString(info));
  1934. var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
  1935. if (provider == null)
  1936. {
  1937. throw new ResourceNotFoundException();
  1938. }
  1939. var configurable = provider as IConfigurableTunerHost;
  1940. if (configurable != null)
  1941. {
  1942. await configurable.Validate(info).ConfigureAwait(false);
  1943. }
  1944. var config = GetConfiguration();
  1945. var list = config.TunerHosts.ToList();
  1946. var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
  1947. if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
  1948. {
  1949. info.Id = Guid.NewGuid().ToString("N");
  1950. list.Add(info);
  1951. config.TunerHosts = list.ToArray();
  1952. }
  1953. else
  1954. {
  1955. config.TunerHosts[index] = info;
  1956. }
  1957. _config.SaveConfiguration("livetv", config);
  1958. if (dataSourceChanged)
  1959. {
  1960. _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
  1961. }
  1962. return info;
  1963. }
  1964. public async Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings)
  1965. {
  1966. // Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider
  1967. // ServerConfiguration.SaveConfiguration crashes during xml serialization for AddListingProvider
  1968. info = _jsonSerializer.DeserializeFromString<ListingsProviderInfo>(_jsonSerializer.SerializeToString(info));
  1969. IListingsProvider provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
  1970. if (provider == null)
  1971. {
  1972. throw new ResourceNotFoundException(
  1973. string.Format("Couldn't find provider of type: '{0}'", info.Type)
  1974. );
  1975. }
  1976. await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false);
  1977. LiveTvOptions config = GetConfiguration();
  1978. List<ListingsProviderInfo> list = config.ListingProviders.ToList();
  1979. int index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
  1980. if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
  1981. {
  1982. info.Id = Guid.NewGuid().ToString("N");
  1983. list.Add(info);
  1984. config.ListingProviders = list.ToArray();
  1985. }
  1986. else
  1987. {
  1988. config.ListingProviders[index] = info;
  1989. }
  1990. _config.SaveConfiguration("livetv", config);
  1991. _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
  1992. return info;
  1993. }
  1994. public void DeleteListingsProvider(string id)
  1995. {
  1996. var config = GetConfiguration();
  1997. config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
  1998. _config.SaveConfiguration("livetv", config);
  1999. _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
  2000. }
  2001. public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelId, string providerChannelId)
  2002. {
  2003. var config = GetConfiguration();
  2004. var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
  2005. listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelId, StringComparison.OrdinalIgnoreCase)).ToArray();
  2006. if (!string.Equals(tunerChannelId, providerChannelId, StringComparison.OrdinalIgnoreCase))
  2007. {
  2008. var list = listingsProviderInfo.ChannelMappings.ToList();
  2009. list.Add(new NameValuePair
  2010. {
  2011. Name = tunerChannelId,
  2012. Value = providerChannelId
  2013. });
  2014. listingsProviderInfo.ChannelMappings = list.ToArray();
  2015. }
  2016. _config.SaveConfiguration("livetv", config);
  2017. var tunerChannels = await GetChannelsForListingsProvider(providerId, CancellationToken.None)
  2018. .ConfigureAwait(false);
  2019. var providerChannels = await GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
  2020. .ConfigureAwait(false);
  2021. var mappings = listingsProviderInfo.ChannelMappings;
  2022. var tunerChannelMappings =
  2023. tunerChannels.Select(i => GetTunerChannelMapping(i, mappings, providerChannels)).ToList();
  2024. _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
  2025. return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelId, StringComparison.OrdinalIgnoreCase));
  2026. }
  2027. public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, NameValuePair[] mappings, List<ChannelInfo> epgChannels)
  2028. {
  2029. var result = new TunerChannelMapping
  2030. {
  2031. Name = tunerChannel.Name,
  2032. Id = tunerChannel.Id
  2033. };
  2034. if (!string.IsNullOrWhiteSpace(tunerChannel.Number))
  2035. {
  2036. result.Name = tunerChannel.Number + " " + result.Name;
  2037. }
  2038. var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, epgChannels);
  2039. if (providerChannel != null)
  2040. {
  2041. result.ProviderChannelName = providerChannel.Name;
  2042. result.ProviderChannelId = providerChannel.Id;
  2043. }
  2044. return result;
  2045. }
  2046. public Task<List<NameIdPair>> GetLineups(string providerType, string providerId, string country, string location)
  2047. {
  2048. var config = GetConfiguration();
  2049. if (string.IsNullOrWhiteSpace(providerId))
  2050. {
  2051. var provider = _listingProviders.FirstOrDefault(i => string.Equals(providerType, i.Type, StringComparison.OrdinalIgnoreCase));
  2052. if (provider == null)
  2053. {
  2054. throw new ResourceNotFoundException();
  2055. }
  2056. return provider.GetLineups(null, country, location);
  2057. }
  2058. else
  2059. {
  2060. var info = config.ListingProviders.FirstOrDefault(i => string.Equals(i.Id, providerId, StringComparison.OrdinalIgnoreCase));
  2061. var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
  2062. if (provider == null)
  2063. {
  2064. throw new ResourceNotFoundException();
  2065. }
  2066. return provider.GetLineups(info, country, location);
  2067. }
  2068. }
  2069. public Task<MBRegistrationRecord> GetRegistrationInfo(string feature)
  2070. {
  2071. if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase))
  2072. {
  2073. feature = "embytvseriesrecordings";
  2074. }
  2075. if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase))
  2076. {
  2077. var config = GetConfiguration();
  2078. if (config.TunerHosts.Length > 0 &&
  2079. config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
  2080. {
  2081. return Task.FromResult(new MBRegistrationRecord
  2082. {
  2083. IsRegistered = true,
  2084. IsValid = true
  2085. });
  2086. }
  2087. }
  2088. return _security.GetRegistrationStatus(feature);
  2089. }
  2090. public Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
  2091. {
  2092. var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
  2093. return EmbyTV.EmbyTV.Current.GetChannelsForListingsProvider(info, cancellationToken);
  2094. }
  2095. public Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken)
  2096. {
  2097. var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
  2098. var provider = _listingProviders.First(i => string.Equals(i.Type, info.Type, StringComparison.OrdinalIgnoreCase));
  2099. return provider.GetChannels(info, cancellationToken);
  2100. }
  2101. public Guid GetInternalChannelId(string serviceName, string externalId)
  2102. {
  2103. return _tvDtoService.GetInternalChannelId(serviceName, externalId);
  2104. }
  2105. public Guid GetInternalProgramId(string externalId)
  2106. {
  2107. return _tvDtoService.GetInternalProgramId(externalId);
  2108. }
  2109. public List<BaseItem> GetRecordingFolders(User user)
  2110. {
  2111. return GetRecordingFolders(user, false);
  2112. }
  2113. private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels)
  2114. {
  2115. var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
  2116. .SelectMany(i => i.Locations)
  2117. .Distinct(StringComparer.OrdinalIgnoreCase)
  2118. .Select(i => _libraryManager.FindByPath(i, true))
  2119. .Where(i => i != null)
  2120. .Where(i => i.IsVisibleStandalone(user))
  2121. .SelectMany(i => _libraryManager.GetCollectionFolders(i))
  2122. .DistinctBy(i => i.Id)
  2123. .OrderBy(i => i.SortName)
  2124. .ToList();
  2125. folders.AddRange(_channelManager().GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
  2126. {
  2127. UserId = user.Id,
  2128. IsRecordingsFolder = true,
  2129. RefreshLatestChannelItems = refreshChannels
  2130. }).Items);
  2131. return folders.Cast<BaseItem>().ToList();
  2132. }
  2133. }
  2134. }