LiveTvManager.cs 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639
  1. using MediaBrowser.Common.Extensions;
  2. using MediaBrowser.Common.IO;
  3. using MediaBrowser.Common.ScheduledTasks;
  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.Entities;
  14. using MediaBrowser.Model.LiveTv;
  15. using MediaBrowser.Model.Logging;
  16. using MediaBrowser.Model.Querying;
  17. using System;
  18. using System.Collections.Concurrent;
  19. using System.Collections.Generic;
  20. using System.IO;
  21. using System.Linq;
  22. using System.Threading;
  23. using System.Threading.Tasks;
  24. namespace MediaBrowser.Server.Implementations.LiveTv
  25. {
  26. /// <summary>
  27. /// Class LiveTvManager
  28. /// </summary>
  29. public class LiveTvManager : ILiveTvManager, IDisposable
  30. {
  31. private readonly IServerConfigurationManager _config;
  32. private readonly IFileSystem _fileSystem;
  33. private readonly ILogger _logger;
  34. private readonly IItemRepository _itemRepo;
  35. private readonly IUserManager _userManager;
  36. private readonly IUserDataManager _userDataManager;
  37. private readonly ILibraryManager _libraryManager;
  38. private readonly ITaskManager _taskManager;
  39. private readonly LiveTvDtoService _tvDtoService;
  40. private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
  41. private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams =
  42. new ConcurrentDictionary<string, LiveStreamInfo>();
  43. private List<Guid> _channelIdList = new List<Guid>();
  44. private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
  45. private readonly ConcurrentDictionary<Guid, bool> _refreshedPrograms = new ConcurrentDictionary<Guid, bool>();
  46. private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1);
  47. public LiveTvManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager)
  48. {
  49. _config = config;
  50. _fileSystem = fileSystem;
  51. _logger = logger;
  52. _itemRepo = itemRepo;
  53. _userManager = userManager;
  54. _libraryManager = libraryManager;
  55. _taskManager = taskManager;
  56. _userDataManager = userDataManager;
  57. _tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
  58. }
  59. /// <summary>
  60. /// Gets the services.
  61. /// </summary>
  62. /// <value>The services.</value>
  63. public IReadOnlyList<ILiveTvService> Services
  64. {
  65. get { return _services; }
  66. }
  67. public ILiveTvService ActiveService { get; private set; }
  68. /// <summary>
  69. /// Adds the parts.
  70. /// </summary>
  71. /// <param name="services">The services.</param>
  72. public void AddParts(IEnumerable<ILiveTvService> services)
  73. {
  74. _services.AddRange(services);
  75. SetActiveService(_services.FirstOrDefault());
  76. }
  77. private void SetActiveService(ILiveTvService service)
  78. {
  79. if (ActiveService != null)
  80. {
  81. ActiveService.DataSourceChanged -= service_DataSourceChanged;
  82. }
  83. ActiveService = service;
  84. if (service != null)
  85. {
  86. service.DataSourceChanged += service_DataSourceChanged;
  87. }
  88. }
  89. void service_DataSourceChanged(object sender, EventArgs e)
  90. {
  91. _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
  92. }
  93. public async Task<QueryResult<ChannelInfoDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
  94. {
  95. var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
  96. var channels = _channelIdList.Select(_libraryManager.GetItemById)
  97. .Where(i => i != null)
  98. .OfType<LiveTvChannel>();
  99. if (user != null)
  100. {
  101. // Avoid implicitly captured closure
  102. var currentUser = user;
  103. channels = channels
  104. .Where(i => i.IsParentalAllowed(currentUser))
  105. .OrderBy(i =>
  106. {
  107. double number = 0;
  108. if (!string.IsNullOrEmpty(i.Number))
  109. {
  110. double.TryParse(i.Number, out number);
  111. }
  112. return number;
  113. });
  114. if (query.IsFavorite.HasValue)
  115. {
  116. var val = query.IsFavorite.Value;
  117. channels = channels
  118. .Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val);
  119. }
  120. }
  121. channels = channels.OrderBy(i =>
  122. {
  123. double number = 0;
  124. if (!string.IsNullOrEmpty(i.Number))
  125. {
  126. double.TryParse(i.Number, out number);
  127. }
  128. return number;
  129. }).ThenBy(i => i.Name);
  130. var allChannels = channels.ToList();
  131. IEnumerable<LiveTvChannel> allEnumerable = allChannels;
  132. if (query.StartIndex.HasValue)
  133. {
  134. allEnumerable = allEnumerable.Skip(query.StartIndex.Value);
  135. }
  136. if (query.Limit.HasValue)
  137. {
  138. allEnumerable = allEnumerable.Take(query.Limit.Value);
  139. }
  140. var returnList = new List<ChannelInfoDto>();
  141. foreach (var channel in allEnumerable)
  142. {
  143. var currentProgram = await GetCurrentProgram(channel.ExternalId, cancellationToken).ConfigureAwait(false);
  144. returnList.Add(_tvDtoService.GetChannelInfoDto(channel, currentProgram, user));
  145. }
  146. var result = new QueryResult<ChannelInfoDto>
  147. {
  148. Items = returnList.ToArray(),
  149. TotalRecordCount = allChannels.Count
  150. };
  151. return result;
  152. }
  153. public LiveTvChannel GetInternalChannel(string id)
  154. {
  155. return GetInternalChannel(new Guid(id));
  156. }
  157. private LiveTvChannel GetInternalChannel(Guid id)
  158. {
  159. return _libraryManager.GetItemById(id) as LiveTvChannel;
  160. }
  161. public async Task<LiveTvProgram> GetInternalProgram(string id, CancellationToken cancellationToken)
  162. {
  163. var guid = new Guid(id);
  164. LiveTvProgram obj = null;
  165. _programs.TryGetValue(guid, out obj);
  166. if (obj != null)
  167. {
  168. await RefreshIfNeeded(obj, cancellationToken).ConfigureAwait(false);
  169. }
  170. return obj;
  171. }
  172. private async Task RefreshIfNeeded(IEnumerable<LiveTvProgram> programs, CancellationToken cancellationToken)
  173. {
  174. foreach (var program in programs)
  175. {
  176. await RefreshIfNeeded(program, cancellationToken).ConfigureAwait(false);
  177. }
  178. }
  179. private async Task RefreshIfNeeded(LiveTvProgram program, CancellationToken cancellationToken)
  180. {
  181. if (_refreshedPrograms.ContainsKey(program.Id))
  182. {
  183. return;
  184. }
  185. await program.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
  186. _refreshedPrograms.TryAdd(program.Id, true);
  187. }
  188. public async Task<ILiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
  189. {
  190. var service = ActiveService;
  191. var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
  192. var recording = recordings.FirstOrDefault(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
  193. return await GetRecording(recording, service.Name, cancellationToken).ConfigureAwait(false);
  194. }
  195. private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
  196. public async Task<LiveStreamInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
  197. {
  198. await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  199. try
  200. {
  201. var service = ActiveService;
  202. var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
  203. var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
  204. var result = await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
  205. if (!string.IsNullOrEmpty(result.Id))
  206. {
  207. _openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
  208. }
  209. return result;
  210. }
  211. catch (Exception ex)
  212. {
  213. _logger.ErrorException("Error getting recording stream", ex);
  214. throw;
  215. }
  216. finally
  217. {
  218. _liveStreamSemaphore.Release();
  219. }
  220. }
  221. public async Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken)
  222. {
  223. await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  224. try
  225. {
  226. var service = ActiveService;
  227. var channel = GetInternalChannel(id);
  228. _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
  229. var result = await service.GetChannelStream(channel.ExternalId, cancellationToken).ConfigureAwait(false);
  230. if (!string.IsNullOrEmpty(result.Id))
  231. {
  232. _openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
  233. }
  234. return result;
  235. }
  236. catch (Exception ex)
  237. {
  238. _logger.ErrorException("Error getting channel stream", ex);
  239. throw;
  240. }
  241. finally
  242. {
  243. _liveStreamSemaphore.Release();
  244. }
  245. }
  246. private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
  247. {
  248. var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "tvchannels", _fileSystem.GetValidFilename(channelInfo.Name));
  249. var fileInfo = new DirectoryInfo(path);
  250. var isNew = false;
  251. if (!fileInfo.Exists)
  252. {
  253. _logger.Debug("Creating directory {0}", path);
  254. Directory.CreateDirectory(path);
  255. fileInfo = new DirectoryInfo(path);
  256. if (!fileInfo.Exists)
  257. {
  258. throw new IOException("Path not created: " + path);
  259. }
  260. isNew = true;
  261. }
  262. var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id);
  263. var item = _itemRepo.RetrieveItem(id) as LiveTvChannel;
  264. if (item == null)
  265. {
  266. item = new LiveTvChannel
  267. {
  268. Name = channelInfo.Name,
  269. Id = id,
  270. DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo),
  271. DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo),
  272. Path = path
  273. };
  274. isNew = true;
  275. }
  276. item.ChannelType = channelInfo.ChannelType;
  277. item.ProviderImageUrl = channelInfo.ImageUrl;
  278. item.HasProviderImage = channelInfo.HasImage;
  279. item.ProviderImagePath = channelInfo.ImagePath;
  280. item.ExternalId = channelInfo.Id;
  281. item.ServiceName = serviceName;
  282. item.Number = channelInfo.Number;
  283. if (string.IsNullOrEmpty(item.Name))
  284. {
  285. item.Name = channelInfo.Name;
  286. }
  287. await item.RefreshMetadata(new MetadataRefreshOptions
  288. {
  289. ForceSave = isNew
  290. }, cancellationToken);
  291. return item;
  292. }
  293. private LiveTvProgram GetProgram(ProgramInfo info, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
  294. {
  295. var id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
  296. var item = _itemRepo.RetrieveItem(id) as LiveTvProgram;
  297. if (item == null)
  298. {
  299. item = new LiveTvProgram
  300. {
  301. Name = info.Name,
  302. Id = id,
  303. DateCreated = DateTime.UtcNow,
  304. DateModified = DateTime.UtcNow
  305. };
  306. }
  307. item.ChannelType = channelType;
  308. item.ServiceName = serviceName;
  309. item.Audio = info.Audio;
  310. item.ExternalChannelId = info.ChannelId;
  311. item.CommunityRating = info.CommunityRating;
  312. item.EndDate = info.EndDate;
  313. item.EpisodeTitle = info.EpisodeTitle;
  314. item.ExternalId = info.Id;
  315. item.Genres = info.Genres;
  316. item.HasProviderImage = info.HasImage;
  317. item.IsHD = info.IsHD;
  318. item.IsKids = info.IsKids;
  319. item.IsLive = info.IsLive;
  320. item.IsMovie = info.IsMovie;
  321. item.IsNews = info.IsNews;
  322. item.IsPremiere = info.IsPremiere;
  323. item.IsRepeat = info.IsRepeat;
  324. item.IsSeries = info.IsSeries;
  325. item.IsSports = info.IsSports;
  326. item.Name = info.Name;
  327. item.OfficialRating = info.OfficialRating;
  328. item.Overview = info.Overview;
  329. item.PremiereDate = info.OriginalAirDate;
  330. item.ProviderImagePath = info.ImagePath;
  331. item.ProviderImageUrl = info.ImageUrl;
  332. item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
  333. item.StartDate = info.StartDate;
  334. return item;
  335. }
  336. private async Task<ILiveTvRecording> GetRecording(RecordingInfo info, string serviceName, CancellationToken cancellationToken)
  337. {
  338. var isNew = false;
  339. var id = _tvDtoService.GetInternalRecordingId(serviceName, info.Id);
  340. var item = _itemRepo.RetrieveItem(id) as ILiveTvRecording;
  341. if (item == null)
  342. {
  343. if (info.ChannelType == ChannelType.TV)
  344. {
  345. item = new LiveTvVideoRecording
  346. {
  347. Name = info.Name,
  348. Id = id,
  349. DateCreated = DateTime.UtcNow,
  350. DateModified = DateTime.UtcNow,
  351. VideoType = VideoType.VideoFile
  352. };
  353. }
  354. else
  355. {
  356. item = new LiveTvAudioRecording
  357. {
  358. Name = info.Name,
  359. Id = id,
  360. DateCreated = DateTime.UtcNow,
  361. DateModified = DateTime.UtcNow
  362. };
  363. }
  364. if (!string.IsNullOrEmpty(info.Path))
  365. {
  366. item.Path = info.Path;
  367. }
  368. else if (!string.IsNullOrEmpty(info.Url))
  369. {
  370. item.Path = info.Url;
  371. }
  372. isNew = true;
  373. }
  374. item.RecordingInfo = info;
  375. item.ServiceName = serviceName;
  376. await item.RefreshMetadata(new MetadataRefreshOptions
  377. {
  378. ForceSave = isNew
  379. }, cancellationToken);
  380. _libraryManager.RegisterItem((BaseItem)item);
  381. return item;
  382. }
  383. private LiveTvChannel GetChannel(LiveTvProgram program)
  384. {
  385. var programChannelId = program.ExternalChannelId;
  386. var internalProgramChannelId = _tvDtoService.GetInternalChannelId(program.ServiceName, programChannelId);
  387. return GetInternalChannel(internalProgramChannelId);
  388. }
  389. public async Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
  390. {
  391. var program = await GetInternalProgram(id, cancellationToken).ConfigureAwait(false);
  392. var channel = GetChannel(program);
  393. var dto = _tvDtoService.GetProgramInfoDto(program, channel, user);
  394. await AddRecordingInfo(new[] { dto }, cancellationToken).ConfigureAwait(false);
  395. return dto;
  396. }
  397. public async Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
  398. {
  399. IEnumerable<LiveTvProgram> programs = _programs.Values;
  400. if (query.MinEndDate.HasValue)
  401. {
  402. var val = query.MinEndDate.Value;
  403. programs = programs.Where(i => i.EndDate.HasValue && i.EndDate.Value >= val);
  404. }
  405. if (query.MinStartDate.HasValue)
  406. {
  407. var val = query.MinStartDate.Value;
  408. programs = programs.Where(i => i.StartDate >= val);
  409. }
  410. if (query.MaxEndDate.HasValue)
  411. {
  412. var val = query.MaxEndDate.Value;
  413. programs = programs.Where(i => i.EndDate.HasValue && i.EndDate.Value <= val);
  414. }
  415. if (query.MaxStartDate.HasValue)
  416. {
  417. var val = query.MaxStartDate.Value;
  418. programs = programs.Where(i => i.StartDate <= val);
  419. }
  420. if (query.ChannelIdList.Length > 0)
  421. {
  422. var guids = query.ChannelIdList.Select(i => new Guid(i)).ToList();
  423. var serviceName = ActiveService.Name;
  424. programs = programs.Where(i =>
  425. {
  426. var programChannelId = i.ExternalChannelId;
  427. var internalProgramChannelId = _tvDtoService.GetInternalChannelId(serviceName, programChannelId);
  428. return guids.Contains(internalProgramChannelId);
  429. });
  430. }
  431. var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
  432. if (user != null)
  433. {
  434. // Avoid implicitly captured closure
  435. var currentUser = user;
  436. programs = programs.Where(i => i.IsParentalAllowed(currentUser));
  437. }
  438. var programList = programs.ToList();
  439. var returnArray = programList
  440. .Select(i =>
  441. {
  442. var channel = GetChannel(i);
  443. return _tvDtoService.GetProgramInfoDto(i, channel, user);
  444. })
  445. .ToArray();
  446. await RefreshIfNeeded(programList, cancellationToken).ConfigureAwait(false);
  447. await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
  448. var result = new QueryResult<ProgramInfoDto>
  449. {
  450. Items = returnArray,
  451. TotalRecordCount = returnArray.Length
  452. };
  453. return result;
  454. }
  455. public async Task<QueryResult<ProgramInfoDto>> GetRecommendedPrograms(RecommendedProgramQuery query, CancellationToken cancellationToken)
  456. {
  457. IEnumerable<LiveTvProgram> programs = _programs.Values;
  458. var user = _userManager.GetUserById(new Guid(query.UserId));
  459. // Avoid implicitly captured closure
  460. var currentUser = user;
  461. programs = programs.Where(i => i.IsParentalAllowed(currentUser));
  462. if (query.IsAiring.HasValue)
  463. {
  464. var val = query.IsAiring.Value;
  465. programs = programs.Where(i => i.IsAiring == val);
  466. }
  467. if (query.HasAired.HasValue)
  468. {
  469. var val = query.HasAired.Value;
  470. programs = programs.Where(i => i.HasAired == val);
  471. }
  472. var serviceName = ActiveService.Name;
  473. var programList = programs.ToList();
  474. var genres = programList.SelectMany(i => i.Genres)
  475. .Distinct(StringComparer.OrdinalIgnoreCase)
  476. .Select(i => _libraryManager.GetGenre(i))
  477. .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
  478. programs = programList.OrderBy(i => i.HasImage(ImageType.Primary) ? 0 : 1)
  479. .ThenByDescending(i => GetRecommendationScore(i, user.Id, serviceName, genres))
  480. .ThenBy(i => i.StartDate);
  481. if (query.Limit.HasValue)
  482. {
  483. programs = programs.Take(query.Limit.Value)
  484. .OrderBy(i => i.StartDate);
  485. }
  486. programList = programs.ToList();
  487. await RefreshIfNeeded(programList, cancellationToken).ConfigureAwait(false);
  488. var returnArray = programList
  489. .Select(i =>
  490. {
  491. var channel = GetChannel(i);
  492. return _tvDtoService.GetProgramInfoDto(i, channel, user);
  493. })
  494. .ToArray();
  495. await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
  496. var result = new QueryResult<ProgramInfoDto>
  497. {
  498. Items = returnArray,
  499. TotalRecordCount = returnArray.Length
  500. };
  501. return result;
  502. }
  503. private int GetRecommendationScore(LiveTvProgram program, Guid userId, string serviceName, Dictionary<string, Genre> genres)
  504. {
  505. var score = 0;
  506. if (program.IsLive)
  507. {
  508. score++;
  509. }
  510. if (program.IsSeries && !program.IsRepeat)
  511. {
  512. score++;
  513. }
  514. var internalChannelId = _tvDtoService.GetInternalChannelId(serviceName, program.ExternalChannelId);
  515. var channel = GetInternalChannel(internalChannelId);
  516. var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey());
  517. if ((channelUserdata.Likes ?? false))
  518. {
  519. score += 2;
  520. }
  521. else if (!(channelUserdata.Likes ?? true))
  522. {
  523. score -= 2;
  524. }
  525. if (channelUserdata.IsFavorite)
  526. {
  527. score += 3;
  528. }
  529. score += GetGenreScore(program.Genres, userId, genres);
  530. return score;
  531. }
  532. private int GetGenreScore(IEnumerable<string> programGenres, Guid userId, Dictionary<string, Genre> genres)
  533. {
  534. return programGenres.Select(i =>
  535. {
  536. var score = 0;
  537. Genre genre;
  538. if (genres.TryGetValue(i, out genre))
  539. {
  540. var genreUserdata = _userDataManager.GetUserData(userId, genre.GetUserDataKey());
  541. if ((genreUserdata.Likes ?? false))
  542. {
  543. score++;
  544. }
  545. else if (!(genreUserdata.Likes ?? true))
  546. {
  547. score--;
  548. }
  549. if (genreUserdata.IsFavorite)
  550. {
  551. score += 2;
  552. }
  553. }
  554. return score;
  555. }).Sum();
  556. }
  557. private async Task AddRecordingInfo(IEnumerable<ProgramInfoDto> programs, CancellationToken cancellationToken)
  558. {
  559. var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
  560. var timerList = timers.ToList();
  561. foreach (var program in programs)
  562. {
  563. var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, program.ExternalId, StringComparison.OrdinalIgnoreCase));
  564. if (timer != null)
  565. {
  566. program.TimerId = _tvDtoService.GetInternalTimerId(program.ServiceName, timer.Id)
  567. .ToString("N");
  568. if (!string.IsNullOrEmpty(timer.SeriesTimerId))
  569. {
  570. program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(program.ServiceName, timer.SeriesTimerId)
  571. .ToString("N");
  572. }
  573. }
  574. }
  575. }
  576. internal async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
  577. {
  578. await _refreshSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  579. try
  580. {
  581. await RefreshChannelsInternal(progress, cancellationToken).ConfigureAwait(false);
  582. }
  583. finally
  584. {
  585. _refreshSemaphore.Release();
  586. }
  587. }
  588. private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
  589. {
  590. // Avoid implicitly captured closure
  591. var service = ActiveService;
  592. if (service == null)
  593. {
  594. progress.Report(100);
  595. return;
  596. }
  597. progress.Report(10);
  598. var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false);
  599. var allChannelsList = allChannels.ToList();
  600. var list = new List<LiveTvChannel>();
  601. var numComplete = 0;
  602. foreach (var channelInfo in allChannelsList)
  603. {
  604. cancellationToken.ThrowIfCancellationRequested();
  605. try
  606. {
  607. var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, cancellationToken).ConfigureAwait(false);
  608. list.Add(item);
  609. _libraryManager.RegisterItem(item);
  610. }
  611. catch (OperationCanceledException)
  612. {
  613. throw;
  614. }
  615. catch (Exception ex)
  616. {
  617. _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Item2.Name);
  618. }
  619. numComplete++;
  620. double percent = numComplete;
  621. percent /= allChannelsList.Count;
  622. progress.Report(5 * percent + 10);
  623. }
  624. _channelIdList = list.Select(i => i.Id).ToList();
  625. progress.Report(15);
  626. numComplete = 0;
  627. var programs = new List<LiveTvProgram>();
  628. var guideDays = GetGuideDays(list.Count);
  629. cancellationToken.ThrowIfCancellationRequested();
  630. foreach (var item in list)
  631. {
  632. cancellationToken.ThrowIfCancellationRequested();
  633. // Avoid implicitly captured closure
  634. var currentChannel = item;
  635. try
  636. {
  637. var start = DateTime.UtcNow.AddHours(-1);
  638. var end = start.AddDays(guideDays);
  639. var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
  640. var programEntities = channelPrograms.Select(program => GetProgram(program, currentChannel.ChannelType, service.Name, cancellationToken));
  641. programs.AddRange(programEntities);
  642. }
  643. catch (OperationCanceledException)
  644. {
  645. throw;
  646. }
  647. catch (Exception ex)
  648. {
  649. _logger.ErrorException("Error getting programs for channel {0}", ex, currentChannel.Name);
  650. }
  651. numComplete++;
  652. double percent = numComplete;
  653. percent /= allChannelsList.Count;
  654. progress.Report(80 * percent + 10);
  655. }
  656. _programs = programs.ToDictionary(i => i.Id);
  657. _refreshedPrograms.Clear();
  658. progress.Report(90);
  659. // Load these now which will prefetch metadata
  660. await GetRecordings(new RecordingQuery(), cancellationToken).ConfigureAwait(false);
  661. progress.Report(100);
  662. }
  663. public async Task CleanDatabase(IProgress<double> progress, CancellationToken cancellationToken)
  664. {
  665. var service = ActiveService;
  666. if (service == null)
  667. {
  668. progress.Report(100);
  669. return;
  670. }
  671. await _refreshSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  672. try
  673. {
  674. await DeleteOldPrograms(_programs.Keys.ToList(), progress, cancellationToken).ConfigureAwait(false);
  675. }
  676. finally
  677. {
  678. _refreshSemaphore.Release();
  679. }
  680. }
  681. private async Task DeleteOldPrograms(List<Guid> currentIdList, IProgress<double> progress, CancellationToken cancellationToken)
  682. {
  683. var list = _itemRepo.GetItemsOfType(typeof(LiveTvProgram)).ToList();
  684. var numComplete = 0;
  685. foreach (var program in list)
  686. {
  687. cancellationToken.ThrowIfCancellationRequested();
  688. if (!currentIdList.Contains(program.Id))
  689. {
  690. await _libraryManager.DeleteItem(program).ConfigureAwait(false);
  691. }
  692. numComplete++;
  693. double percent = numComplete;
  694. percent /= list.Count;
  695. progress.Report(100 * percent);
  696. }
  697. }
  698. private double GetGuideDays(int channelCount)
  699. {
  700. if (_config.Configuration.LiveTvOptions.GuideDays.HasValue)
  701. {
  702. return _config.Configuration.LiveTvOptions.GuideDays.Value;
  703. }
  704. var programsPerDay = channelCount * 48;
  705. const int maxPrograms = 24000;
  706. var days = Math.Round(((double)maxPrograms) / programsPerDay);
  707. // No less than 2, no more than 7
  708. return Math.Max(2, Math.Min(days, 7));
  709. }
  710. private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
  711. {
  712. var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false);
  713. return channels.Select(i => new Tuple<string, ChannelInfo>(service.Name, i));
  714. }
  715. public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
  716. {
  717. var service = ActiveService;
  718. var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
  719. var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
  720. if (!string.IsNullOrEmpty(query.ChannelId))
  721. {
  722. var guid = new Guid(query.ChannelId);
  723. var currentServiceName = service.Name;
  724. recordings = recordings
  725. .Where(i => _tvDtoService.GetInternalChannelId(currentServiceName, i.ChannelId) == guid);
  726. }
  727. if (!string.IsNullOrEmpty(query.Id))
  728. {
  729. var guid = new Guid(query.Id);
  730. var currentServiceName = service.Name;
  731. recordings = recordings
  732. .Where(i => _tvDtoService.GetInternalRecordingId(currentServiceName, i.Id) == guid);
  733. }
  734. if (!string.IsNullOrEmpty(query.GroupId))
  735. {
  736. var guid = new Guid(query.GroupId);
  737. recordings = recordings.Where(i => GetRecordingGroupIds(i).Contains(guid));
  738. }
  739. if (query.IsInProgress.HasValue)
  740. {
  741. var val = query.IsInProgress.Value;
  742. recordings = recordings.Where(i => (i.Status == RecordingStatus.InProgress) == val);
  743. }
  744. if (query.Status.HasValue)
  745. {
  746. var val = query.Status.Value;
  747. recordings = recordings.Where(i => (i.Status == val));
  748. }
  749. if (!string.IsNullOrEmpty(query.SeriesTimerId))
  750. {
  751. var guid = new Guid(query.SeriesTimerId);
  752. var currentServiceName = service.Name;
  753. recordings = recordings
  754. .Where(i => _tvDtoService.GetInternalSeriesTimerId(currentServiceName, i.SeriesTimerId) == guid);
  755. }
  756. recordings = recordings.OrderByDescending(i => i.StartDate);
  757. IEnumerable<ILiveTvRecording> entities = await GetEntities(recordings, service.Name, cancellationToken).ConfigureAwait(false);
  758. if (user != null)
  759. {
  760. var currentUser = user;
  761. entities = entities.Where(i => i.IsParentalAllowed(currentUser));
  762. }
  763. var entityList = entities.ToList();
  764. entities = entityList;
  765. if (query.StartIndex.HasValue)
  766. {
  767. entities = entities.Skip(query.StartIndex.Value);
  768. }
  769. if (query.Limit.HasValue)
  770. {
  771. entities = entities.Take(query.Limit.Value);
  772. }
  773. var returnArray = entities
  774. .Select(i =>
  775. {
  776. var channel = string.IsNullOrEmpty(i.RecordingInfo.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, i.RecordingInfo.ChannelId));
  777. return _tvDtoService.GetRecordingInfoDto(i, channel, service, user);
  778. })
  779. .ToArray();
  780. return new QueryResult<RecordingInfoDto>
  781. {
  782. Items = returnArray,
  783. TotalRecordCount = entityList.Count
  784. };
  785. }
  786. private Task<ILiveTvRecording[]> GetEntities(IEnumerable<RecordingInfo> recordings, string serviceName, CancellationToken cancellationToken)
  787. {
  788. var tasks = recordings.Select(i => GetRecording(i, serviceName, cancellationToken));
  789. return Task.WhenAll(tasks);
  790. }
  791. private IEnumerable<ILiveTvService> GetServices(string serviceName, string channelId)
  792. {
  793. IEnumerable<ILiveTvService> services = _services;
  794. if (string.IsNullOrEmpty(serviceName) && !string.IsNullOrEmpty(channelId))
  795. {
  796. var channel = GetInternalChannel(channelId);
  797. if (channel != null)
  798. {
  799. serviceName = channel.ServiceName;
  800. }
  801. }
  802. if (!string.IsNullOrEmpty(serviceName))
  803. {
  804. services = services.Where(i => string.Equals(i.Name, serviceName, StringComparison.OrdinalIgnoreCase));
  805. }
  806. return services;
  807. }
  808. public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
  809. {
  810. var service = ActiveService;
  811. var timers = await service.GetTimersAsync(cancellationToken).ConfigureAwait(false);
  812. if (!string.IsNullOrEmpty(query.ChannelId))
  813. {
  814. var guid = new Guid(query.ChannelId);
  815. timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId));
  816. }
  817. if (!string.IsNullOrEmpty(query.SeriesTimerId))
  818. {
  819. var guid = new Guid(query.SeriesTimerId);
  820. var currentServiceName = service.Name;
  821. timers = timers
  822. .Where(i => _tvDtoService.GetInternalSeriesTimerId(currentServiceName, i.SeriesTimerId) == guid);
  823. }
  824. var returnList = new List<TimerInfoDto>();
  825. foreach (var i in timers)
  826. {
  827. var program = string.IsNullOrEmpty(i.ProgramId) ?
  828. null :
  829. await GetInternalProgram(_tvDtoService.GetInternalProgramId(service.Name, i.ProgramId).ToString("N"), cancellationToken).ConfigureAwait(false);
  830. var channel = string.IsNullOrEmpty(i.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, i.ChannelId));
  831. returnList.Add(_tvDtoService.GetTimerInfoDto(i, service, program, channel));
  832. }
  833. var returnArray = returnList
  834. .OrderBy(i => i.StartDate)
  835. .ToArray();
  836. return new QueryResult<TimerInfoDto>
  837. {
  838. Items = returnArray,
  839. TotalRecordCount = returnArray.Length
  840. };
  841. }
  842. public async Task DeleteRecording(string recordingId)
  843. {
  844. var recording = await GetRecording(recordingId, CancellationToken.None).ConfigureAwait(false);
  845. if (recording == null)
  846. {
  847. throw new ResourceNotFoundException(string.Format("Recording with Id {0} not found", recordingId));
  848. }
  849. var service = GetServices(recording.ServiceName, null)
  850. .First();
  851. await service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None).ConfigureAwait(false);
  852. }
  853. public async Task CancelTimer(string id)
  854. {
  855. var timer = await GetTimer(id, CancellationToken.None).ConfigureAwait(false);
  856. if (timer == null)
  857. {
  858. throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
  859. }
  860. var service = GetServices(timer.ServiceName, null)
  861. .First();
  862. await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
  863. }
  864. public async Task CancelSeriesTimer(string id)
  865. {
  866. var timer = await GetSeriesTimer(id, CancellationToken.None).ConfigureAwait(false);
  867. if (timer == null)
  868. {
  869. throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
  870. }
  871. var service = GetServices(timer.ServiceName, null)
  872. .First();
  873. await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
  874. }
  875. public async Task<RecordingInfoDto> GetRecording(string id, CancellationToken cancellationToken, User user = null)
  876. {
  877. var results = await GetRecordings(new RecordingQuery
  878. {
  879. UserId = user == null ? null : user.Id.ToString("N"),
  880. Id = id
  881. }, cancellationToken).ConfigureAwait(false);
  882. return results.Items.FirstOrDefault();
  883. }
  884. public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
  885. {
  886. var results = await GetTimers(new TimerQuery(), cancellationToken).ConfigureAwait(false);
  887. return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture));
  888. }
  889. public async Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken)
  890. {
  891. var results = await GetSeriesTimers(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
  892. return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture));
  893. }
  894. public async Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken)
  895. {
  896. var service = ActiveService;
  897. var timers = await service.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
  898. if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase))
  899. {
  900. timers = query.SortOrder == SortOrder.Descending ?
  901. timers.OrderBy(i => i.Priority).ThenByStringDescending(i => i.Name) :
  902. timers.OrderByDescending(i => i.Priority).ThenByString(i => i.Name);
  903. }
  904. else
  905. {
  906. timers = query.SortOrder == SortOrder.Descending ?
  907. timers.OrderByStringDescending(i => i.Name) :
  908. timers.OrderByString(i => i.Name);
  909. }
  910. var returnArray = timers
  911. .Select(i =>
  912. {
  913. string channelName = null;
  914. if (!string.IsNullOrEmpty(i.ChannelId))
  915. {
  916. var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId);
  917. var channel = GetInternalChannel(internalChannelId);
  918. channelName = channel == null ? null : channel.Name;
  919. }
  920. return _tvDtoService.GetSeriesTimerInfoDto(i, service, channelName);
  921. })
  922. .ToArray();
  923. return new QueryResult<SeriesTimerInfoDto>
  924. {
  925. Items = returnArray,
  926. TotalRecordCount = returnArray.Length
  927. };
  928. }
  929. public async Task<ChannelInfoDto> GetChannel(string id, CancellationToken cancellationToken, User user = null)
  930. {
  931. var channel = GetInternalChannel(id);
  932. var currentProgram = await GetCurrentProgram(channel.ExternalId, cancellationToken).ConfigureAwait(false);
  933. var dto = _tvDtoService.GetChannelInfoDto(channel, currentProgram, user);
  934. return dto;
  935. }
  936. private async Task<LiveTvProgram> GetCurrentProgram(string externalChannelId, CancellationToken cancellationToken)
  937. {
  938. var now = DateTime.UtcNow;
  939. var program = _programs.Values
  940. .Where(i => string.Equals(externalChannelId, i.ExternalChannelId, StringComparison.OrdinalIgnoreCase))
  941. .OrderBy(i => i.StartDate)
  942. .SkipWhile(i => now >= (i.EndDate ?? DateTime.MinValue))
  943. .FirstOrDefault();
  944. if (program != null)
  945. {
  946. await RefreshIfNeeded(program, cancellationToken).ConfigureAwait(false);
  947. }
  948. return program;
  949. }
  950. private async Task<SeriesTimerInfo> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
  951. {
  952. ProgramInfo programInfo = null;
  953. if (program != null)
  954. {
  955. programInfo = new ProgramInfo
  956. {
  957. Audio = program.Audio,
  958. ChannelId = program.ExternalChannelId,
  959. CommunityRating = program.CommunityRating,
  960. EndDate = program.EndDate ?? DateTime.MinValue,
  961. EpisodeTitle = program.EpisodeTitle,
  962. Genres = program.Genres,
  963. HasImage = program.HasProviderImage,
  964. Id = program.ExternalId,
  965. IsHD = program.IsHD,
  966. IsKids = program.IsKids,
  967. IsLive = program.IsLive,
  968. IsMovie = program.IsMovie,
  969. IsNews = program.IsNews,
  970. IsPremiere = program.IsPremiere,
  971. IsRepeat = program.IsRepeat,
  972. IsSeries = program.IsSeries,
  973. IsSports = program.IsSports,
  974. OriginalAirDate = program.PremiereDate,
  975. Overview = program.Overview,
  976. StartDate = program.StartDate,
  977. ImagePath = program.ProviderImagePath,
  978. ImageUrl = program.ProviderImageUrl,
  979. Name = program.Name,
  980. OfficialRating = program.OfficialRating
  981. };
  982. }
  983. var info = await ActiveService.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
  984. info.Id = null;
  985. return info;
  986. }
  987. public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(CancellationToken cancellationToken)
  988. {
  989. var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
  990. var obj = _tvDtoService.GetSeriesTimerInfoDto(info, ActiveService, null);
  991. return obj;
  992. }
  993. public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
  994. {
  995. var program = await GetInternalProgram(programId, cancellationToken).ConfigureAwait(false);
  996. var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false);
  997. var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
  998. var info = _tvDtoService.GetSeriesTimerInfoDto(defaults, ActiveService, null);
  999. info.Days = new List<DayOfWeek>
  1000. {
  1001. program.StartDate.ToLocalTime().DayOfWeek
  1002. };
  1003. info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
  1004. info.Name = program.Name;
  1005. info.ChannelId = programDto.ChannelId;
  1006. info.ChannelName = programDto.ChannelName;
  1007. info.StartDate = program.StartDate;
  1008. info.Name = program.Name;
  1009. info.Overview = program.Overview;
  1010. info.ProgramId = programDto.Id;
  1011. info.ExternalProgramId = programDto.ExternalId;
  1012. if (program.EndDate.HasValue)
  1013. {
  1014. info.EndDate = program.EndDate.Value;
  1015. }
  1016. return info;
  1017. }
  1018. public async Task CreateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
  1019. {
  1020. var service = string.IsNullOrEmpty(timer.ServiceName) ? ActiveService : GetServices(timer.ServiceName, null).First();
  1021. var info = await _tvDtoService.GetTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
  1022. // Set priority from default values
  1023. var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
  1024. info.Priority = defaultValues.Priority;
  1025. await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1026. }
  1027. public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
  1028. {
  1029. var service = string.IsNullOrEmpty(timer.ServiceName) ? ActiveService : GetServices(timer.ServiceName, null).First();
  1030. var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
  1031. // Set priority from default values
  1032. var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
  1033. info.Priority = defaultValues.Priority;
  1034. await service.CreateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1035. }
  1036. public async Task UpdateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
  1037. {
  1038. var info = await _tvDtoService.GetTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
  1039. var service = string.IsNullOrEmpty(timer.ServiceName) ? ActiveService : GetServices(timer.ServiceName, null).First();
  1040. await service.UpdateTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1041. }
  1042. public async Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
  1043. {
  1044. var info = await _tvDtoService.GetSeriesTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
  1045. var service = string.IsNullOrEmpty(timer.ServiceName) ? ActiveService : GetServices(timer.ServiceName, null).First();
  1046. await service.UpdateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
  1047. }
  1048. private IEnumerable<string> GetRecordingGroupNames(RecordingInfo recording)
  1049. {
  1050. var list = new List<string>();
  1051. if (recording.IsSeries)
  1052. {
  1053. list.Add(recording.Name);
  1054. }
  1055. if (recording.IsKids)
  1056. {
  1057. list.Add("Kids");
  1058. }
  1059. if (recording.IsMovie)
  1060. {
  1061. list.Add("Movies");
  1062. }
  1063. if (recording.IsNews)
  1064. {
  1065. list.Add("News");
  1066. }
  1067. if (recording.IsSports)
  1068. {
  1069. list.Add("Sports");
  1070. }
  1071. if (!recording.IsSports && !recording.IsNews && !recording.IsMovie && !recording.IsKids && !recording.IsSeries)
  1072. {
  1073. list.Add("Others");
  1074. }
  1075. return list;
  1076. }
  1077. private List<Guid> GetRecordingGroupIds(RecordingInfo recording)
  1078. {
  1079. return GetRecordingGroupNames(recording).Select(i => i.ToLower()
  1080. .GetMD5())
  1081. .ToList();
  1082. }
  1083. public async Task<QueryResult<RecordingGroupDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken)
  1084. {
  1085. var recordingResult = await GetRecordings(new RecordingQuery
  1086. {
  1087. UserId = query.UserId
  1088. }, cancellationToken).ConfigureAwait(false);
  1089. var recordings = recordingResult.Items;
  1090. var groups = new List<RecordingGroupDto>();
  1091. var series = recordings
  1092. .Where(i => i.IsSeries)
  1093. .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase)
  1094. .ToList();
  1095. groups.AddRange(series.OrderByString(i => i.Key).Select(i => new RecordingGroupDto
  1096. {
  1097. Name = i.Key,
  1098. RecordingCount = i.Count()
  1099. }));
  1100. groups.Add(new RecordingGroupDto
  1101. {
  1102. Name = "Kids",
  1103. RecordingCount = recordings.Count(i => i.IsKids)
  1104. });
  1105. groups.Add(new RecordingGroupDto
  1106. {
  1107. Name = "Movies",
  1108. RecordingCount = recordings.Count(i => i.IsMovie)
  1109. });
  1110. groups.Add(new RecordingGroupDto
  1111. {
  1112. Name = "News",
  1113. RecordingCount = recordings.Count(i => i.IsNews)
  1114. });
  1115. groups.Add(new RecordingGroupDto
  1116. {
  1117. Name = "Sports",
  1118. RecordingCount = recordings.Count(i => i.IsSports)
  1119. });
  1120. groups.Add(new RecordingGroupDto
  1121. {
  1122. Name = "Others",
  1123. RecordingCount = recordings.Count(i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries)
  1124. });
  1125. groups = groups
  1126. .Where(i => i.RecordingCount > 0)
  1127. .ToList();
  1128. foreach (var group in groups)
  1129. {
  1130. group.Id = group.Name.ToLower().GetMD5().ToString("N");
  1131. }
  1132. return new QueryResult<RecordingGroupDto>
  1133. {
  1134. Items = groups.ToArray(),
  1135. TotalRecordCount = groups.Count
  1136. };
  1137. }
  1138. public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
  1139. {
  1140. await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  1141. var service = ActiveService;
  1142. _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id);
  1143. try
  1144. {
  1145. await service.CloseLiveStream(id, cancellationToken).ConfigureAwait(false);
  1146. }
  1147. catch (Exception ex)
  1148. {
  1149. _logger.ErrorException("Error closing live stream", ex);
  1150. throw;
  1151. }
  1152. finally
  1153. {
  1154. _liveStreamSemaphore.Release();
  1155. }
  1156. }
  1157. public GuideInfo GetGuideInfo()
  1158. {
  1159. var programs = _programs.ToList();
  1160. var startDate = _programs.Count == 0 ? DateTime.MinValue :
  1161. programs.Select(i => i.Value.StartDate).Min();
  1162. var endDate = programs.Count == 0 ? DateTime.MinValue :
  1163. programs.Select(i => i.Value.StartDate).Max();
  1164. return new GuideInfo
  1165. {
  1166. StartDate = startDate,
  1167. EndDate = endDate
  1168. };
  1169. }
  1170. /// <summary>
  1171. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  1172. /// </summary>
  1173. public void Dispose()
  1174. {
  1175. Dispose(true);
  1176. }
  1177. private readonly object _disposeLock = new object();
  1178. /// <summary>
  1179. /// Releases unmanaged and - optionally - managed resources.
  1180. /// </summary>
  1181. /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  1182. protected virtual void Dispose(bool dispose)
  1183. {
  1184. if (dispose)
  1185. {
  1186. lock (_disposeLock)
  1187. {
  1188. foreach (var stream in _openStreams.Values.ToList())
  1189. {
  1190. var task = CloseLiveStream(stream.Id, CancellationToken.None);
  1191. Task.WaitAll(task);
  1192. }
  1193. _openStreams.Clear();
  1194. }
  1195. }
  1196. }
  1197. private async Task<IEnumerable<LiveTvServiceInfo>> GetServiceInfos(CancellationToken cancellationToken)
  1198. {
  1199. var tasks = Services.Select(i => GetServiceInfo(i, cancellationToken));
  1200. return await Task.WhenAll(tasks).ConfigureAwait(false);
  1201. }
  1202. private async Task<LiveTvServiceInfo> GetServiceInfo(ILiveTvService service, CancellationToken cancellationToken)
  1203. {
  1204. var info = new LiveTvServiceInfo
  1205. {
  1206. Name = service.Name
  1207. };
  1208. try
  1209. {
  1210. var statusInfo = await service.GetStatusInfoAsync(cancellationToken).ConfigureAwait(false);
  1211. info.Status = statusInfo.Status;
  1212. info.StatusMessage = statusInfo.StatusMessage;
  1213. info.Version = statusInfo.Version;
  1214. info.HasUpdateAvailable = statusInfo.HasUpdateAvailable;
  1215. info.HomePageUrl = service.HomePageUrl;
  1216. info.Tuners = statusInfo.Tuners.Select(i =>
  1217. {
  1218. string channelName = null;
  1219. if (!string.IsNullOrEmpty(i.ChannelId))
  1220. {
  1221. var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId);
  1222. var channel = GetInternalChannel(internalChannelId);
  1223. channelName = channel == null ? null : channel.Name;
  1224. }
  1225. return _tvDtoService.GetTunerInfoDto(service.Name, i, channelName);
  1226. }).ToList();
  1227. }
  1228. catch (Exception ex)
  1229. {
  1230. _logger.ErrorException("Error getting service status info from {0}", ex, service.Name);
  1231. info.Status = LiveTvServiceStatus.Unavailable;
  1232. info.StatusMessage = ex.Message;
  1233. }
  1234. return info;
  1235. }
  1236. public async Task<LiveTvInfo> GetLiveTvInfo(CancellationToken cancellationToken)
  1237. {
  1238. var services = await GetServiceInfos(CancellationToken.None).ConfigureAwait(false);
  1239. var servicesList = services.ToList();
  1240. var activeServiceInfo = ActiveService == null ? null :
  1241. servicesList.FirstOrDefault(i => string.Equals(i.Name, ActiveService.Name, StringComparison.OrdinalIgnoreCase));
  1242. var info = new LiveTvInfo
  1243. {
  1244. Services = servicesList.ToList(),
  1245. ActiveServiceName = activeServiceInfo == null ? null : activeServiceInfo.Name,
  1246. IsEnabled = ActiveService != null,
  1247. Status = activeServiceInfo == null ? LiveTvServiceStatus.Unavailable : activeServiceInfo.Status,
  1248. StatusMessage = activeServiceInfo == null ? null : activeServiceInfo.StatusMessage
  1249. };
  1250. info.EnabledUsers = _userManager.Users
  1251. .Where(i => i.Configuration.EnableLiveTvAccess && info.IsEnabled)
  1252. .Select(i => i.Id.ToString("N"))
  1253. .ToList();
  1254. return info;
  1255. }
  1256. /// <summary>
  1257. /// Resets the tuner.
  1258. /// </summary>
  1259. /// <param name="id">The identifier.</param>
  1260. /// <param name="cancellationToken">The cancellation token.</param>
  1261. /// <returns>Task.</returns>
  1262. public Task ResetTuner(string id, CancellationToken cancellationToken)
  1263. {
  1264. return ActiveService.ResetTuner(id, cancellationToken);
  1265. }
  1266. }
  1267. }