Ejemplo n.º 1
0
        private IEnumerable <TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer, IEnumerable <ProgramInfo> allPrograms, IReadOnlyList <RecordingInfo> currentRecordings)
        {
            if (seriesTimer == null)
            {
                throw new ArgumentNullException("seriesTimer");
            }
            if (allPrograms == null)
            {
                throw new ArgumentNullException("allPrograms");
            }
            if (currentRecordings == null)
            {
                throw new ArgumentNullException("currentRecordings");
            }

            // Exclude programs that have already ended
            allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow && i.StartDate > DateTime.UtcNow);

            allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);

            var recordingShowIds = currentRecordings.Select(i => i.ProgramId).Where(i => !string.IsNullOrWhiteSpace(i)).ToList();

            allPrograms = allPrograms.Where(i => !recordingShowIds.Contains(i.Id, StringComparer.OrdinalIgnoreCase));

            return(allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer)));
        }
Ejemplo n.º 2
0
        public override void Update(TimerInfo item)
        {
            base.Update(item);

            Timer timer;

            if (_timers.TryGetValue(item.Id, out timer))
            {
                var timespan = RecordingHelper.GetStartTime(item) - DateTime.UtcNow;
                timer.Change(timespan, TimeSpan.Zero);
            }
            else
            {
                AddTimer(item);
            }
        }
Ejemplo n.º 3
0
        private void AddTimer(TimerInfo item)
        {
            var startDate = RecordingHelper.GetStartTime(item);
            var now       = DateTime.UtcNow;

            if (startDate < now)
            {
                EventHelper.FireEventIfNotNull(TimerFired, this, new GenericEventArgs <TimerInfo> {
                    Argument = item
                }, Logger);
                return;
            }

            var timerLength = startDate - now;

            StartTimer(item, timerLength);
        }
Ejemplo n.º 4
0
        private void ScheduleWake(TimerInfo info)
        {
            var startDate = RecordingHelper.GetStartTime(info).AddMinutes(-5);

            try
            {
                _powerManagement.ScheduleWake(startDate);
                _logger.Info("Scheduled system wake timer at {0} (UTC)", startDate);
            }
            catch (NotImplementedException)
            {
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Error scheduling wake timer", ex);
            }
        }
Ejemplo n.º 5
0
        private void AddOrUpdateSystemTimer(TimerInfo item)
        {
            StopTimer(item);

            if (!ShouldStartTimer(item))
            {
                return;
            }

            var startDate = RecordingHelper.GetStartTime(item);
            var now       = DateTime.UtcNow;

            if (startDate < now)
            {
                EventHelper.FireEventIfNotNull(TimerFired, this, new GenericEventArgs <TimerInfo> {
                    Argument = item
                }, Logger);
                return;
            }

            var dueTime = startDate - now;

            StartTimer(item, dueTime);
        }
Ejemplo n.º 6
0
        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.AddOrUpdate(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.AddOrUpdate(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.AddOrUpdate(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);
            }
        }
Ejemplo n.º 7
0
        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;
            }
            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);
            }
        }
Ejemplo n.º 8
0
        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 = 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);

            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.AddOrUpdate(recording);
            }

            try
            {
                var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false);

                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;

                    var recorder = await GetRecorder().ConfigureAwait(false);

                    if (recorder is EncodedRecorder)
                    {
                        recordPath = Path.ChangeExtension(recordPath, ".mp4");
                    }
                    recordPath = EnsureFileUnique(recordPath, timer.Id);
                    _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
                    activeRecordingInfo.Path = recordPath;

                    _libraryMonitor.ReportFileSystemChangeBeginning(recordPath);

                    recording.Path            = recordPath;
                    recording.Status          = RecordingStatus.InProgress;
                    recording.DateLastUpdated = DateTime.UtcNow;
                    _recordingProvider.AddOrUpdate(recording);

                    _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 = () =>
                    {
                        result.Item2.Release();
                        isResourceOpen = false;
                    };

                    await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);

                    recording.Status = RecordingStatus.Completed;
                    _logger.Info("Recording completed: {0}", recordPath);
                }
                finally
                {
                    if (isResourceOpen)
                    {
                        result.Item2.Release();
                    }

                    _libraryMonitor.ReportFileSystemChangeComplete(recordPath, false);
                }
            }
            catch (OperationCanceledException)
            {
                _logger.Info("Recording stopped: {0}", recordPath);
                recording.Status = RecordingStatus.Completed;
            }
            catch (Exception ex)
            {
                _logger.ErrorException("Error recording to {0}", ex, recordPath);
                recording.Status = RecordingStatus.Error;
            }
            finally
            {
                ActiveRecordingInfo removed;
                _activeRecordings.TryRemove(timer.Id, out removed);
            }

            recording.DateLastUpdated = DateTime.UtcNow;
            _recordingProvider.AddOrUpdate(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);
            }
        }