public static string GetRecordingName(TimerInfo timer, ProgramInfo info) { if (info == null) { return (timer.ProgramId + ".ts"); } var fancyName = info.Name; if (info.ProductionYear != null) { fancyName += "_(" + info.ProductionYear + ")"; } if (info.IsSeries) { fancyName += "_" + info.EpisodeTitle.Replace("Season: ", "S").Replace(" Episode: ", "E"); } if (info.IsHD ?? false) { fancyName += "_HD"; } if (info.OriginalAirDate != null) { fancyName += "_" + info.OriginalAirDate.Value.ToString("yyyy-MM-dd"); } return StringHelper.RemoveSpecialCharacters(fancyName) + ".ts"; }
public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service) { var dto = new TimerInfoDto { Id = GetInternalTimerId(service.Name, info.Id).ToString("N"), ChannelName = info.ChannelName, Overview = info.Overview, EndDate = info.EndDate, Name = info.Name, StartDate = info.StartDate, ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId, info.ChannelName).ToString("N"), Status = info.Status, SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"), PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds, IsPostPaddingRequired = info.IsPostPaddingRequired, IsPrePaddingRequired = info.IsPrePaddingRequired, ExternalChannelId = info.ChannelId, ExternalSeriesTimerId = info.SeriesTimerId, ServiceName = service.Name, ExternalProgramId = info.ProgramId, Priority = info.Priority }; var duration = info.EndDate - info.StartDate; dto.DurationMs = Convert.ToInt32(duration.TotalMilliseconds); if (!string.IsNullOrEmpty(info.ProgramId)) { dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N"); } return dto; }
public static List<TimerInfo> GetTimers(Stream stream, IJsonSerializer json, ILogger logger) { List<TimerInfo> ret = new List<TimerInfo>(); var root = ParseRecRules(stream, json); foreach (var item in root.RecRuleList.RecRules) { if (!item.Inactive && string.Compare(item.Type, "Not Recording", true) != 0) { TimerInfo val = new TimerInfo() { Name = item.Title, Overview = item.Description, ChannelId = item.ChanId.ToString(), EndDate = (DateTime)item.EndTime, StartDate = (DateTime)item.StartTime, Id = item.Id, PrePaddingSeconds = item.StartOffset * 60, PostPaddingSeconds = item.EndOffset * 60, IsPostPaddingRequired = item.EndOffset != 0, IsPrePaddingRequired = item.StartOffset != 0, ProgramId = item.ProgramId }; ret.Add(val); } } return ret; }
public void CopyTimer(TimerInfo parent) { foreach (PropertyInfo prop in parent.GetType().GetProperties()) { GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(parent, null), null); } this.Id = parent.ProgramId; }
public static DateTime GetStartTime(TimerInfo timer) { if (timer.StartDate.AddSeconds(-timer.PrePaddingSeconds + 1) < DateTime.UtcNow) { return DateTime.UtcNow.AddSeconds(1); } return timer.StartDate.AddSeconds(-timer.PrePaddingSeconds); }
public async Task CreateTimer(TimerInfo info, CancellationToken cancellationToken) { try { } catch (Exception exception) { Logger.ErrorException("HTSP create timer failed", exception); } }
public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel) { var dto = new TimerInfoDto { Id = GetInternalTimerId(service.Name, info.Id).ToString("N"), Overview = info.Overview, EndDate = info.EndDate, Name = info.Name, StartDate = info.StartDate, ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), Status = info.Status, SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"), PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds, IsPostPaddingRequired = info.IsPostPaddingRequired, IsPrePaddingRequired = info.IsPrePaddingRequired, KeepUntil = info.KeepUntil, ExternalChannelId = info.ChannelId, ExternalSeriesTimerId = info.SeriesTimerId, ServiceName = service.Name, ExternalProgramId = info.ProgramId, Priority = info.Priority, RunTimeTicks = (info.EndDate - info.StartDate).Ticks, ServerId = _appHost.SystemId }; if (!string.IsNullOrEmpty(info.ProgramId)) { dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N"); } if (program != null) { dto.ProgramInfo = _dtoService.GetBaseItemDto(program, new DtoOptions()); if (info.Status != RecordingStatus.Cancelled && info.Status != RecordingStatus.Error) { dto.ProgramInfo.TimerId = dto.Id; dto.ProgramInfo.Status = info.Status.ToString(); } dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId; } if (channel != null) { dto.ChannelName = channel.Name; } return dto; }
public static string GetRecordingName(TimerInfo timer, ProgramInfo info) { if (info == null) { return timer.ProgramId; } var name = info.Name; if (info.IsSeries) { var addHyphen = true; if (info.SeasonNumber.HasValue && info.EpisodeNumber.HasValue) { name += string.Format(" S{0}E{1}", info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture)); addHyphen = false; } else if (info.OriginalAirDate.HasValue) { name += " " + info.OriginalAirDate.Value.ToString("yyyy-MM-dd"); } else { name += " " + DateTime.Now.ToString("yyyy-MM-dd"); } if (!string.IsNullOrWhiteSpace(info.EpisodeTitle)) { if (addHyphen) { name += " -"; } name += " " + info.EpisodeTitle; } } else if (info.IsMovie && info.ProductionYear != null) { name += " (" + info.ProductionYear + ")"; } else { name += " " + info.StartDate.ToString("yyyy-MM-dd") + " " + info.Id; } return name; }
public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel) { var dto = new TimerInfoDto { Id = GetInternalTimerId(service.Name, info.Id).ToString("N"), Overview = info.Overview, EndDate = info.EndDate, Name = info.Name, StartDate = info.StartDate, ExternalId = info.Id, ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"), Status = info.Status, SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"), PrePaddingSeconds = info.PrePaddingSeconds, PostPaddingSeconds = info.PostPaddingSeconds, IsPostPaddingRequired = info.IsPostPaddingRequired, IsPrePaddingRequired = info.IsPrePaddingRequired, ExternalChannelId = info.ChannelId, ExternalSeriesTimerId = info.SeriesTimerId, ServiceName = service.Name, ExternalProgramId = info.ProgramId, Priority = info.Priority, RunTimeTicks = (info.EndDate - info.StartDate).Ticks }; if (!string.IsNullOrEmpty(info.ProgramId)) { dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N"); } if (program != null) { dto.ProgramInfo = GetProgramInfoDto(program, channel); dto.ProgramInfo.TimerId = dto.Id; dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId; } if (channel != null) { dto.ChannelName = channel.Name; } return dto; }
public static void CopyProgramInfoToTimerInfo(ProgramInfo programInfo, TimerInfo timerInfo) { timerInfo.SeasonNumber = programInfo.SeasonNumber; timerInfo.EpisodeNumber = programInfo.EpisodeNumber; timerInfo.IsMovie = programInfo.IsMovie; timerInfo.IsKids = programInfo.IsKids; timerInfo.IsNews = programInfo.IsNews; timerInfo.IsSports = programInfo.IsSports; timerInfo.ProductionYear = programInfo.ProductionYear; timerInfo.EpisodeTitle = programInfo.EpisodeTitle; timerInfo.OriginalAirDate = programInfo.OriginalAirDate; timerInfo.IsProgramSeries = programInfo.IsSeries; timerInfo.HomePageUrl = programInfo.HomePageUrl; timerInfo.CommunityRating = programInfo.CommunityRating; timerInfo.ShortOverview = programInfo.ShortOverview; timerInfo.OfficialRating = programInfo.OfficialRating; timerInfo.IsRepeat = programInfo.IsRepeat; }
public static TimerInfo CreateTimer(ProgramInfo parent, SeriesTimerInfo series) { var timer = new TimerInfo(); timer.ChannelId = parent.ChannelId; timer.Id = (series.Id + parent.Id).GetMD5().ToString("N"); timer.StartDate = parent.StartDate; timer.EndDate = parent.EndDate; timer.ProgramId = parent.Id; timer.PrePaddingSeconds = series.PrePaddingSeconds; timer.PostPaddingSeconds = series.PostPaddingSeconds; timer.IsPostPaddingRequired = series.IsPostPaddingRequired; timer.IsPrePaddingRequired = series.IsPrePaddingRequired; timer.Priority = series.Priority; timer.Name = parent.Name; timer.Overview = parent.Overview; timer.SeriesTimerId = series.Id; return timer; }
public async Task CreateTimer(TimerInfo info, CancellationToken cancellationToken) { try { var channel = new Channel().ById(info.ChannelId, cancellationToken).Result; var timer = "{\"channel\":\"" + channel.Uuid + "\",\"disp_title\":\"" + info.Name + "\",\"disp_description\":\"" + info.Overview + "\",\"start\":" + Helper.Converter.DatetimeToLinuxSeconds(info.StartDate) + ",\"stop\":" + Helper.Converter.DatetimeToLinuxSeconds(info.EndDate) + ",\"start_extra\":" + (info.PrePaddingSeconds / 60) + ",\"stop_extra\":" + (info.PostPaddingSeconds / 60) + "\"}"; await Post(Url.Dvr.Create, timer, cancellationToken); } catch (Exception exception) { Logger.ErrorException("Creating timer DVR error, API call failed", exception); } }
public void CreateSchedule(CancellationToken cancellationToken, TimerInfo timer) { var programData = GetProgram(cancellationToken, timer.ProgramId); if (programData == null) { throw ExceptionHelper.CreateArgumentException("timer.ProgramId", "The program id {0} could not be found", timer.ProgramId); } var builder = new StringBuilder("AddScheduleDetailed?"); builder.AppendFormat("channelid={0}&", programData.ChannelId); builder.AppendFormat("title={0}&", programData.Title); builder.AppendFormat("starttime={0}&", programData.StartTime.ToLocalTime().ToUrlDate()); builder.AppendFormat("endtime={0}&", programData.EndTime.ToLocalTime().ToUrlDate()); builder.AppendFormat("scheduletype={0}&", (Int32)WebScheduleType.Once); if (timer.IsPrePaddingRequired & timer.PrePaddingSeconds > 0) { builder.AppendFormat("preRecordInterval={0}&", timer.PrePaddingSeconds / 60); } if (timer.IsPostPaddingRequired & timer.PostPaddingSeconds > 0) { builder.AppendFormat("postRecordInterval={0}&", timer.PostPaddingSeconds / 60); } builder.Remove(builder.Length - 1, 1); Plugin.Logger.Info("Creating scheule with StartTime: {0}, EndTime: {1}, ProgramData from MP: {2}", timer.StartDate, timer.EndDate, builder.ToString()); var response = GetFromService<WebBoolResult>(cancellationToken, builder.ToString()); if (!response.Result) { throw new LiveTvConflictException(); } }
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { info.Id = Guid.NewGuid().ToString("N"); _timerProvider.Add(info); return Task.FromResult(0); }
private async Task RecordStream(TimerInfo timer, CancellationToken cancellationToken) { var mediaStreamInfo = await GetChannelStream(timer.ChannelId, "none", CancellationToken.None); var duration = (timer.EndDate - RecordingHelper.GetStartTime(timer)).TotalSeconds + timer.PrePaddingSeconds; HttpRequestOptions httpRequestOptions = new HttpRequestOptionsMod() { Url = mediaStreamInfo.Path + "?duration=" + duration }; var info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); var recordPath = RecordingPath; if (info.IsMovie) { recordPath = Path.Combine(recordPath, "Movies", StringHelper.RemoveSpecialCharacters(info.Name)); } else { recordPath = Path.Combine(recordPath, "TV", StringHelper.RemoveSpecialCharacters(info.Name)); } recordPath = Path.Combine(recordPath, RecordingHelper.GetRecordingName(timer, info)); Directory.CreateDirectory(Path.GetDirectoryName(recordPath)); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, info.Id, StringComparison.OrdinalIgnoreCase)); if (recording == null) { recording = new RecordingInfo() { ChannelId = info.ChannelId, Id = info.Id, StartDate = info.StartDate, EndDate = info.EndDate, Genres = info.Genres ?? null, IsKids = info.IsKids, IsLive = info.IsLive, IsMovie = info.IsMovie, IsHD = info.IsHD, IsNews = info.IsNews, IsPremiere = info.IsPremiere, IsSeries = info.IsSeries, IsSports = info.IsSports, IsRepeat = !info.IsPremiere, Name = info.Name, EpisodeTitle = info.EpisodeTitle ?? "", ProgramId = info.Id, HasImage = info.HasImage ?? false, ImagePath = info.ImagePath ?? null, ImageUrl = info.ImageUrl, OriginalAirDate = info.OriginalAirDate, Status = RecordingStatus.Scheduled, Overview = info.Overview, SeriesTimerId = info.Id.Substring(0, 10) }; _recordingProvider.Add(recording); } recording.Path = recordPath; recording.Status = RecordingStatus.InProgress; _recordingProvider.Update(recording); try { httpRequestOptions.BufferContent = false; httpRequestOptions.CancellationToken = cancellationToken; _logger.Info("Writing file to path: " + recordPath); using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET")) { using (var output = File.Open(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { await response.Content.CopyToAsync(output, 4096, cancellationToken); } } recording.Status = RecordingStatus.Completed; } catch (OperationCanceledException) { recording.Status = RecordingStatus.Cancelled; } catch { recording.Status = RecordingStatus.Error; } _recordingProvider.Update(recording); _timerProvider.Delete(timer); _logger.Info("Recording was a success"); }
public async Task<TimerInfo> GetTimerInfo(TimerInfoDto dto, bool isNew, LiveTvManager liveTv, CancellationToken cancellationToken) { var info = new TimerInfo { Overview = dto.Overview, EndDate = dto.EndDate, Name = dto.Name, StartDate = dto.StartDate, Status = dto.Status, PrePaddingSeconds = dto.PrePaddingSeconds, PostPaddingSeconds = dto.PostPaddingSeconds, IsPostPaddingRequired = dto.IsPostPaddingRequired, IsPrePaddingRequired = dto.IsPrePaddingRequired, Priority = dto.Priority, SeriesTimerId = dto.ExternalSeriesTimerId, ProgramId = dto.ExternalProgramId, ChannelId = dto.ExternalChannelId, Id = dto.ExternalId }; // Convert internal server id's to external tv provider id's if (!isNew && !string.IsNullOrEmpty(dto.Id) && string.IsNullOrEmpty(info.Id)) { var timer = await liveTv.GetSeriesTimer(dto.Id, cancellationToken).ConfigureAwait(false); info.Id = timer.ExternalId; } if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId)) { var channel = liveTv.GetInternalChannel(dto.ChannelId); if (channel != null) { info.ChannelId = channel.ExternalId; } } if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId)) { var program = liveTv.GetInternalProgram(dto.ProgramId); if (program != null) { info.ProgramId = program.ExternalId; } } if (!string.IsNullOrEmpty(dto.SeriesTimerId) && string.IsNullOrEmpty(info.SeriesTimerId)) { var timer = await liveTv.GetSeriesTimer(dto.SeriesTimerId, cancellationToken).ConfigureAwait(false); if (timer != null) { info.SeriesTimerId = timer.ExternalId; } } return info; }
private TimerInfo GetTimerInfo(EpgEventJSONObject i) { var info = new TimerInfo(); var recurr = i.recurr; if (recurr != null) { if (recurr.OID != 0) { info.SeriesTimerId = recurr.OID.ToString(_usCulture); } info.Name = recurr.RecurringName; } var schd = i.schd; if (schd != null) { info.ChannelId = schd.ChannelOid.ToString(_usCulture); info.Id = schd.OID.ToString(_usCulture); info.Status = ParseStatus(schd.Status); info.StartDate = DateTime.Parse(schd.StartTime).ToUniversalTime(); info.EndDate = DateTime.Parse(schd.EndTime).ToUniversalTime(); info.PrePaddingSeconds = int.Parse(schd.PrePadding, _usCulture) * 60; info.PostPaddingSeconds = int.Parse(schd.PostPadding, _usCulture) * 60; info.Name = schd.Name; } var epg = i.epgEvent; if (epg != null) { //info.Audio = ListingsResponse.ParseAudio(epg.Audio); info.ProgramId = epg.OID.ToString(_usCulture); //info.OfficialRating = epg.Rating; //info.EpisodeTitle = epg.Subtitle; info.Name = epg.Title; info.Overview = epg.Desc; //info.Genres = epg.Genres; //info.IsRepeat = !epg.FirstRun; //info.CommunityRating = ListingsResponse.ParseCommunityRating(epg.StarRating); //info.IsHD = string.Equals(epg.Quality, "hdtv", StringComparison.OrdinalIgnoreCase); } return info; }
/// <summary> /// Update a single Timer /// </summary> /// <param name="info">The program info</param> /// <param name="cancellationToken">The CancellationToken</param> /// <returns></returns> public async Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { _logger.Info(string.Format("[NextPvr] Start UpdateTimer Async for ChannelId: {0} & Name: {1}", info.ChannelId, info.Name)); await EnsureConnectionAsync(cancellationToken).ConfigureAwait(false); var baseUrl = Plugin.Instance.Configuration.WebServiceUrl; var options = new HttpRequestOptions { CancellationToken = cancellationToken, Url = string.Format("{0}/public/ScheduleService/UpdateRec?sid={1}", baseUrl, Sid) }; var timerSettings = await GetDefaultScheduleSettings(cancellationToken).ConfigureAwait(false); timerSettings.scheduleOID = int.Parse(info.Id); timerSettings.post_padding_min = info.PostPaddingSeconds / 60; timerSettings.pre_padding_min = info.PrePaddingSeconds / 60; var postContent = _jsonSerializer.SerializeToString(timerSettings); UtilsHelper.DebugInformation(_logger, string.Format("[NextPvr] TimerSettings UpdateTimer: {0} for ChannelId: {1} & Name: {2}", postContent, info.ChannelId, info.Name)); options.RequestContent = postContent; options.RequestContentType = "application/json"; try { await _httpClient.Post(options).ConfigureAwait((false)); } catch (HttpException ex) { _logger.Error(string.Format("[NextPvr] UpdateTimer Async with exception: {0}", ex.Message)); throw new LiveTvConflictException(); } }
public async Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { Logger.Debug("LiveTvService.UpdateTimerAsync"); await Task.Run(() => true, cancellationToken); }
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) { if (timer == null) { throw new ArgumentNullException("timer"); } ProgramInfo info = null; if (string.IsNullOrWhiteSpace(timer.ProgramId)) { _logger.Info("Timer {0} has null programId", timer.Id); } else { info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); } if (info == null) { _logger.Info("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); info = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate); } if (info == null) { throw new InvalidOperationException(string.Format("Program with Id {0} not found", timer.ProgramId)); } var recordPath = GetRecordingPath(timer, info); var recordingStatus = RecordingStatus.New; var isResourceOpen = false; SemaphoreSlim semaphore = null; try { var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false); isResourceOpen = true; semaphore = result.Item3; var mediaStreamInfo = result.Item1; // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); var recorder = await GetRecorder().ConfigureAwait(false); recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath); recordPath = EnsureFileUnique(recordPath, timer.Id); _libraryMonitor.ReportFileSystemChangeBeginning(recordPath); _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); activeRecordingInfo.Path = recordPath; var duration = recordingEndDate - DateTime.UtcNow; _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); _logger.Info("Writing file to path: " + recordPath); _logger.Info("Opening recording stream from tuner provider"); Action onStarted = () => { timer.Status = RecordingStatus.InProgress; _timerProvider.AddOrUpdate(timer, false); result.Item3.Release(); isResourceOpen = false; }; var pathWithDuration = result.Item2.ApplyDuration(mediaStreamInfo.Path, duration); // If it supports supplying duration via url if (!string.Equals(pathWithDuration, mediaStreamInfo.Path, StringComparison.OrdinalIgnoreCase)) { mediaStreamInfo.Path = pathWithDuration; mediaStreamInfo.RunTimeTicks = duration.Ticks; } await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); recordingStatus = RecordingStatus.Completed; _logger.Info("Recording completed: {0}", recordPath); } catch (OperationCanceledException) { _logger.Info("Recording stopped: {0}", recordPath); recordingStatus = RecordingStatus.Completed; } catch (Exception ex) { _logger.ErrorException("Error recording to {0}", ex, recordPath); recordingStatus = RecordingStatus.Error; } finally { if (isResourceOpen && semaphore != null) { semaphore.Release(); } _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true); ActiveRecordingInfo removed; _activeRecordings.TryRemove(timer.Id, out removed); } if (recordingStatus == RecordingStatus.Completed) { timer.Status = RecordingStatus.Completed; _timerProvider.Delete(timer); OnSuccessfulRecording(info.IsSeries, recordPath); } else if (DateTime.UtcNow < timer.EndDate) { const int retryIntervalSeconds = 60; _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds); timer.Status = RecordingStatus.New; timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds); _timerProvider.AddOrUpdate(timer); } else { _timerProvider.Delete(timer); } }
private string GetRecordingPath(TimerInfo timer, ProgramInfo info) { var recordPath = RecordingPath; var config = GetConfiguration(); if (info.IsMovie) { var customRecordingPath = config.MovieRecordingPath; var allowSubfolder = true; if (!string.IsNullOrWhiteSpace(customRecordingPath)) { allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); recordPath = customRecordingPath; } if (allowSubfolder && config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Movies"); } var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); if (info.ProductionYear.HasValue) { folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; } recordPath = Path.Combine(recordPath, folderName); } else if (info.IsSeries) { var customRecordingPath = config.SeriesRecordingPath; var allowSubfolder = true; if (!string.IsNullOrWhiteSpace(customRecordingPath)) { allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); recordPath = customRecordingPath; } if (allowSubfolder && config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Series"); } var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); var folderNameWithYear = folderName; if (info.ProductionYear.HasValue) { folderNameWithYear += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; } if (Directory.Exists(Path.Combine(recordPath, folderName))) { recordPath = Path.Combine(recordPath, folderName); } else { recordPath = Path.Combine(recordPath, folderNameWithYear); } if (info.SeasonNumber.HasValue) { folderName = string.Format("Season {0}", info.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); recordPath = Path.Combine(recordPath, folderName); } } else if (info.IsKids) { if (config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Kids"); } var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); if (info.ProductionYear.HasValue) { folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; } recordPath = Path.Combine(recordPath, folderName); } else if (info.IsSports) { if (config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Sports"); } recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(info.Name).Trim()); } else { if (config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Other"); } recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(info.Name).Trim()); } var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts"; return Path.Combine(recordPath, recordingFileName); }
public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { return CreateTimer(info, cancellationToken); }
private List<ScheduleRule> UpdateRules(List<ScheduleRule> rules, TimerInfo timerInfo, SeriesTimerInfo seriesTimerInfo) { if (timerInfo != null) { //Single Recording SchedulerHelper.AppendTitleRule(rules, 0, timerInfo.Name); SchedulerHelper.AppendChannelsRule(rules, false, new List<Guid> { Guid.Parse(timerInfo.ChannelId) }); SchedulerHelper.AppendOnDateAndDaysOfWeekRule(rules, ScheduleDaysOfWeek.None, timerInfo.StartDate.ToLocalTime()); SchedulerHelper.AppendAroundTimeRule(rules, timerInfo.StartDate.ToLocalTime()); } else if (seriesTimerInfo != null) { //Serie Recording SchedulerHelper.AppendTitleRule(rules, 0, seriesTimerInfo.Name); SchedulerHelper.AppendOnDateAndDaysOfWeekRule(rules, SchedulerHelper.GetScheduleDaysOfWeek(seriesTimerInfo.Days), seriesTimerInfo.StartDate.ToLocalTime()); SchedulerHelper.AppendNewEpisodesOnlyRule(rules, seriesTimerInfo.RecordNewOnly); if (!seriesTimerInfo.RecordAnyTime) { SchedulerHelper.AppendAroundTimeRule(rules, seriesTimerInfo.StartDate.ToLocalTime()); } if (!seriesTimerInfo.RecordAnyChannel) { SchedulerHelper.AppendChannelsRule(rules, false, new List<Guid> { Guid.Parse(seriesTimerInfo.ChannelId) }); } } return rules; }
/// <summary> /// Create a new recording /// </summary> /// <param name="info">The TimerInfo</param> /// <param name="cancellationToken">The cancellationToken</param> /// <returns></returns> public async Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { _logger.Debug("[NextPvr] Start CreateTimer Async"); await EnsureConnectionAsync(cancellationToken).ConfigureAwait(false); var baseUrl = Plugin.Instance.Configuration.WebServiceUrl; var options = new HttpRequestOptions { CancellationToken = cancellationToken, Url = string.Format("{0}/public/ScheduleService/Record?sid={1}", baseUrl,Sid) }; var timerSettings = await GetDefaultScheduleSettings(cancellationToken).ConfigureAwait(false); timerSettings.allChannels = false; timerSettings.ChannelOID = int.Parse(info.ChannelId, _usCulture); if (!string.IsNullOrEmpty(info.ProgramId)) { timerSettings.epgeventOID = int.Parse(info.ProgramId, _usCulture); } timerSettings.post_padding_min = info.PostPaddingSeconds / 60; timerSettings.pre_padding_min = info.PrePaddingSeconds / 60; var postContent = _jsonSerializer.SerializeToString(timerSettings); _logger.Debug("[NextPvr] TimerSettings for CreateTimer: {0}", postContent); options.RequestContent = postContent; options.RequestContentType = "application/json"; try { await _httpClient.Post(options).ConfigureAwait((false)); } catch (HttpException ex) { _logger.Debug("[NextPvr] " + ex.Message); throw new LiveTvConflictException(); } }
public Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { _timerProvider.Update(info); return Task.FromResult(true); }
public Task<IEnumerable<TimerInfo>> buildPendingTimersInfos(CancellationToken cancellationToken) { return Task.Factory.StartNew<IEnumerable<TimerInfo>>(() => { lock (_data) { List<TimerInfo> result = new List<TimerInfo>(); foreach (KeyValuePair<string, HTSMessage> entry in _data) { if (cancellationToken.IsCancellationRequested) { _logger.Info("[TVHclient] DvrDataHelper.buildDvrInfos, call canceled - returning part list."); return result; } HTSMessage m = entry.Value; TimerInfo ti = new TimerInfo(); try { if (m.containsField("id")) { ti.Id = "" + m.getInt("id"); } } catch (InvalidCastException) { } try { if (m.containsField("channel")) { ti.ChannelId = "" + m.getInt("channel"); } } catch (InvalidCastException) { } try { if (m.containsField("start")) { long unixUtc = m.getLong("start"); ti.StartDate = _initialDateTimeUTC.AddSeconds(unixUtc).ToUniversalTime(); } } catch (InvalidCastException) { } try { if (m.containsField("stop")) { long unixUtc = m.getLong("stop"); ti.EndDate = _initialDateTimeUTC.AddSeconds(unixUtc).ToUniversalTime(); } } catch (InvalidCastException) { } try { if (m.containsField("title")) { ti.Name = m.getString("title"); } } catch (InvalidCastException) { } try { if (m.containsField("description")) { ti.Overview = m.getString("description"); } } catch (InvalidCastException) { } try { if (m.containsField("state")) { string state = m.getString("state"); switch (state) { case "scheduled": ti.Status = RecordingStatus.Scheduled; break; default: // only scheduled timers need to be delivered continue; } } } catch (InvalidCastException) { } try { if (m.containsField("startExtra")) { ti.PrePaddingSeconds = (int)m.getLong("startExtra") * 60; ti.IsPrePaddingRequired = true; } } catch (InvalidCastException) { } try { if (m.containsField("stopExtra")) { ti.PostPaddingSeconds = (int)m.getLong("stopExtra") * 60; ti.IsPostPaddingRequired = true; } } catch (InvalidCastException) { } try { if (m.containsField("priority")) { ti.Priority = m.getInt("priority"); } } catch (InvalidCastException) { } try { if (m.containsField("autorecId")) { ti.SeriesTimerId = m.getString("autorecId"); } } catch (InvalidCastException) { } try { if (m.containsField("eventId")) { ti.ProgramId = "" + m.getInt("eventId"); } } catch (InvalidCastException) { } result.Add(ti); } return result; } }); }
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, CancellationToken cancellationToken) { if (timer == null) { throw new ArgumentNullException("timer"); } if (string.IsNullOrWhiteSpace(timer.ProgramId)) { throw new InvalidOperationException("timer.ProgramId is null. Cannot record."); } var info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); if (info == null) { throw new InvalidOperationException(string.Format("Program with Id {0} not found", timer.ProgramId)); } var recordPath = RecordingPath; if (info.IsMovie) { recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name).Trim()); } else if (info.IsSeries) { recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name).Trim()); } else if (info.IsKids) { recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name).Trim()); } else if (info.IsSports) { recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name).Trim()); } else { recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name).Trim()); } var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts"; recordPath = Path.Combine(recordPath, recordingFileName); _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); var recordingId = info.Id.GetMD5().ToString("N"); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, recordingId, StringComparison.OrdinalIgnoreCase)); if (recording == null) { recording = new RecordingInfo { ChannelId = info.ChannelId, Id = recordingId, StartDate = info.StartDate, EndDate = info.EndDate, Genres = info.Genres, IsKids = info.IsKids, IsLive = info.IsLive, IsMovie = info.IsMovie, IsHD = info.IsHD, IsNews = info.IsNews, IsPremiere = info.IsPremiere, IsSeries = info.IsSeries, IsSports = info.IsSports, IsRepeat = !info.IsPremiere, Name = info.Name, EpisodeTitle = info.EpisodeTitle, ProgramId = info.Id, ImagePath = info.ImagePath, ImageUrl = info.ImageUrl, OriginalAirDate = info.OriginalAirDate, Status = RecordingStatus.Scheduled, Overview = info.Overview, SeriesTimerId = timer.SeriesTimerId, TimerId = timer.Id, ShowId = info.ShowId }; _recordingProvider.Add(recording); } try { var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None); var mediaStreamInfo = result.Item1; var isResourceOpen = true; // Unfortunately due to the semaphore we have to have a nested try/finally try { // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg await Task.Delay(3000, cancellationToken).ConfigureAwait(false); var duration = recordingEndDate - DateTime.UtcNow; HttpRequestOptions httpRequestOptions = new HttpRequestOptions() { Url = mediaStreamInfo.Path }; recording.Path = recordPath; recording.Status = RecordingStatus.InProgress; recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.Update(recording); _logger.Info("Beginning recording."); httpRequestOptions.BufferContent = false; var durationToken = new CancellationTokenSource(duration); var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; httpRequestOptions.CancellationToken = linkedToken; _logger.Info("Writing file to path: " + recordPath); using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET")) { using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { result.Item2.Release(); isResourceOpen = false; await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); } } recording.Status = RecordingStatus.Completed; _logger.Info("Recording completed"); } finally { if (isResourceOpen) { result.Item2.Release(); } } } catch (OperationCanceledException) { _logger.Info("Recording stopped"); recording.Status = RecordingStatus.Completed; } catch (Exception ex) { _logger.ErrorException("Error recording", ex); recording.Status = RecordingStatus.Error; } finally { CancellationTokenSource removed; _activeRecordings.TryRemove(timer.Id, out removed); } recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.Update(recording); if (recording.Status == RecordingStatus.Completed) { OnSuccessfulRecording(recording); _timerProvider.Delete(timer); } else if (DateTime.UtcNow < timer.EndDate) { const int retryIntervalSeconds = 60; _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds); _timerProvider.StartTimer(timer, TimeSpan.FromSeconds(retryIntervalSeconds)); } else { _timerProvider.Delete(timer); _recordingProvider.Delete(recording); } }
public async Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken) { Logger.Debug("LiveTvService.CreateTimerAsync"); await (new Htsp.Dvr()).CreateTimer(info, cancellationToken).ConfigureAwait(false); }
public static DateTime GetStartTime(TimerInfo timer) { return timer.StartDate.AddSeconds(-timer.PrePaddingSeconds); }
private async Task RecordStream(TimerInfo timer, CancellationToken cancellationToken) { if (timer == null) { throw new ArgumentNullException("timer"); } var mediaStreamInfo = await GetChannelStream(timer.ChannelId, null, CancellationToken.None); var duration = (timer.EndDate - DateTime.UtcNow).Add(TimeSpan.FromSeconds(timer.PostPaddingSeconds)); HttpRequestOptions httpRequestOptions = new HttpRequestOptions() { Url = mediaStreamInfo.Path }; var info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); var recordPath = RecordingPath; if (info.IsMovie) { recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name)); } else if (info.IsSeries) { recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name)); } else if (info.IsKids) { recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name)); } else if (info.IsSports) { recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name)); } else { recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name)); } var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)) + ".ts"; recordPath = Path.Combine(recordPath, recordingFileName); Directory.CreateDirectory(Path.GetDirectoryName(recordPath)); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.ProgramId, info.Id, StringComparison.OrdinalIgnoreCase)); if (recording == null) { recording = new RecordingInfo { ChannelId = info.ChannelId, Id = Guid.NewGuid().ToString("N"), StartDate = info.StartDate, EndDate = info.EndDate, Genres = info.Genres, IsKids = info.IsKids, IsLive = info.IsLive, IsMovie = info.IsMovie, IsHD = info.IsHD, IsNews = info.IsNews, IsPremiere = info.IsPremiere, IsSeries = info.IsSeries, IsSports = info.IsSports, IsRepeat = !info.IsPremiere, Name = info.Name, EpisodeTitle = info.EpisodeTitle, ProgramId = info.Id, HasImage = info.HasImage, ImagePath = info.ImagePath, ImageUrl = info.ImageUrl, OriginalAirDate = info.OriginalAirDate, Status = RecordingStatus.Scheduled, Overview = info.Overview, SeriesTimerId = timer.SeriesTimerId, TimerId = timer.Id, ShowId = info.ShowId }; _recordingProvider.Add(recording); } recording.Path = recordPath; recording.Status = RecordingStatus.InProgress; recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.Update(recording); _logger.Info("Beginning recording."); try { httpRequestOptions.BufferContent = false; var durationToken = new CancellationTokenSource(duration); var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; httpRequestOptions.CancellationToken = linkedToken; _logger.Info("Writing file to path: " + recordPath); using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET")) { using (var output = File.Open(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); } } recording.Status = RecordingStatus.Completed; _logger.Info("Recording completed"); } catch (OperationCanceledException) { _logger.Info("Recording stopped"); recording.Status = RecordingStatus.Completed; } catch (Exception ex) { _logger.ErrorException("Error recording", ex); recording.Status = RecordingStatus.Error; } recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.Update(recording); _timerProvider.Delete(timer); _logger.Info("Recording was a success"); if (recording.Status == RecordingStatus.Completed) { OnSuccessfulRecording(recording); } }