diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/LiveTv')
9 files changed, 226 insertions, 96 deletions
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 4e0d6e8d4..38b83eb02 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -8,7 +8,9 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; @@ -47,10 +49,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly ILibraryManager _libraryManager; private readonly IProviderManager _providerManager; private readonly IFileOrganizationService _organizationService; + private readonly IMediaEncoder _mediaEncoder; public static EmbyTV Current; - public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService) + public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder) { Current = this; @@ -64,12 +67,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _libraryMonitor = libraryMonitor; _providerManager = providerManager; _organizationService = organizationService; + _mediaEncoder = mediaEncoder; _liveTvManager = (LiveTvManager)liveTvManager; _jsonSerializer = jsonSerializer; - _recordingProvider = new ItemDataProvider<RecordingInfo>(jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)); - _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); - _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers")); + _recordingProvider = new ItemDataProvider<RecordingInfo>(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)); + _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); + _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers")); _timerProvider.TimerFired += _timerProvider_TimerFired; } @@ -126,6 +130,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return status; } + public async Task RefreshSeriesTimers(CancellationToken cancellationToken, IProgress<double> progress) + { + var timers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); + + List<ChannelInfo> channels = null; + + foreach (var timer in timers) + { + List<ProgramInfo> epgData; + + if (timer.RecordAnyChannel) + { + if (channels == null) + { + channels = (await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false)).ToList(); + } + var channelIds = channels.Select(i => i.Id).ToList(); + epgData = GetEpgDataForChannels(channelIds); + } + else + { + epgData = GetEpgDataForChannel(timer.ChannelId); + } + await UpdateTimersForSeriesTimer(epgData, timer).ConfigureAwait(false); + } + } + private List<ChannelInfo> _channelCache = null; private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken) { @@ -235,7 +266,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - File.Delete(remove.Path); + _fileSystem.DeleteFile(remove.Path); } catch (DirectoryNotFoundException) { @@ -366,6 +397,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) { + try + { + return await GetProgramsAsyncInternal(channelId, startDateUtc, endDateUtc, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting programs", ex); + return GetEpgDataForChannel(channelId).Where(i => i.StartDate <= endDateUtc && i.EndDate >= startDateUtc); + } + } + + private async Task<IEnumerable<ProgramInfo>> GetProgramsAsyncInternal(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) + { var channels = await GetChannelsAsync(true, cancellationToken).ConfigureAwait(false); var channel = channels.First(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)); @@ -373,6 +417,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channel.Number, startDateUtc, endDateUtc, cancellationToken) .ConfigureAwait(false); + var list = programs.ToList(); // Replace the value that came from the provider with a normalized value @@ -428,6 +473,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (mediaSourceInfo != null) { + await AddMediaInfo(mediaSourceInfo, false, cancellationToken).ConfigureAwait(false); + mediaSourceInfo.Id = Guid.NewGuid().ToString("N"); return mediaSourceInfo; } @@ -458,6 +505,84 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } + private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) + { + var originalRuntime = mediaSource.RunTimeTicks; + + var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest + { + InputPath = mediaSource.Path, + Protocol = mediaSource.Protocol, + MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, + ExtractChapters = false + + }, cancellationToken).ConfigureAwait(false); + + mediaSource.Bitrate = info.Bitrate; + mediaSource.Container = info.Container; + mediaSource.Formats = info.Formats; + mediaSource.MediaStreams = info.MediaStreams; + mediaSource.RunTimeTicks = info.RunTimeTicks; + mediaSource.Size = info.Size; + mediaSource.Timestamp = info.Timestamp; + mediaSource.Video3DFormat = info.Video3DFormat; + mediaSource.VideoType = info.VideoType; + + mediaSource.DefaultSubtitleStreamIndex = null; + + // Null this out so that it will be treated like a live stream + if (!originalRuntime.HasValue) + { + mediaSource.RunTimeTicks = null; + } + + var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio); + + if (audioStream == null || audioStream.Index == -1) + { + mediaSource.DefaultAudioStreamIndex = null; + } + else + { + mediaSource.DefaultAudioStreamIndex = audioStream.Index; + } + + var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video); + if (videoStream != null) + { + if (!videoStream.BitRate.HasValue) + { + var width = videoStream.Width ?? 1920; + + if (width >= 1900) + { + videoStream.BitRate = 8000000; + } + + else if (width >= 1260) + { + videoStream.BitRate = 3000000; + } + + else if (width >= 700) + { + videoStream.BitRate = 1000000; + } + } + } + + // Try to estimate this + if (!mediaSource.Bitrate.HasValue) + { + var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); + + if (total > 0) + { + mediaSource.Bitrate = total; + } + } + } + public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken) { throw new NotImplementedException(); @@ -500,14 +625,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV catch (Exception ex) { _logger.ErrorException("Error recording stream", ex); - - if (DateTime.UtcNow < timer.EndDate) - { - const int retryIntervalSeconds = 60; - _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds); - - _timerProvider.StartTimer(timer, TimeSpan.FromSeconds(retryIntervalSeconds)); - } } } @@ -553,7 +670,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)) + ".ts"; recordPath = Path.Combine(recordPath, recordingFileName); - Directory.CreateDirectory(Path.GetDirectoryName(recordPath)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.ProgramId, info.Id, StringComparison.OrdinalIgnoreCase)); @@ -597,7 +714,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _recordingProvider.Update(recording); _logger.Info("Beginning recording."); - + try { httpRequestOptions.BufferContent = false; @@ -607,7 +724,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _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)) + using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); } @@ -626,15 +743,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _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); - _timerProvider.Delete(timer); - _logger.Info("Recording was a success"); 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); } } @@ -752,7 +885,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private void SaveEpgDataForChannel(string channelId, List<ProgramInfo> epgData) { var path = GetChannelEpgCachePath(channelId); - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); lock (_epgLock) { _jsonSerializer.SerializeToFile(epgData, path); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 75dec5f97..2c8917ab4 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { @@ -16,13 +17,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV protected readonly ILogger Logger; private readonly string _dataPath; protected readonly Func<T, T, bool> EqualityComparer; + private readonly IFileSystem _fileSystem; - public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer) + public ItemDataProvider(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer) { Logger = logger; _dataPath = dataPath; EqualityComparer = equalityComparer; _jsonSerializer = jsonSerializer; + _fileSystem = fileSystem; } public IReadOnlyList<T> GetAll() @@ -69,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private void UpdateList(List<T> newList) { var file = _dataPath + ".json"; - Directory.CreateDirectory(Path.GetDirectoryName(file)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(file)); lock (_fileDataLock) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs index eab278eb4..563658885 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs @@ -2,13 +2,14 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo> { - public SeriesTimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath) - : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) + public SeriesTimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath) + : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) { } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index 3ae38f382..64a5b7339 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Linq; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { @@ -16,8 +17,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired; - public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath) - : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) + public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath) + : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) { } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index d53c08150..868889ba7 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -216,20 +216,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings // Helper.logger.Info("Modifyin channel " + channel.Number); if (_channelPair.ContainsKey(channel.Number)) { - string channelName; if (_channelPair[channel.Number].logo != null) { channel.ImageUrl = _channelPair[channel.Number].logo.URL; channel.HasImage = true; } - if (_channelPair[channel.Number].affiliate != null) - { - channelName = _channelPair[channel.Number].affiliate; - } - else - { - channelName = _channelPair[channel.Number].name; - } + string channelName = _channelPair[channel.Number].name; channel.Name = channelName; } else @@ -245,8 +237,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings ScheduleDirect.ProgramDetails details) { //_logger.Debug("Show type is: " + (details.showType ?? "No ShowType")); - DateTime startAt = DateTime.ParseExact(programInfo.airDateTime, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", - CultureInfo.InvariantCulture); + DateTime startAt = GetDate(programInfo.airDateTime); DateTime endAt = startAt.AddSeconds(programInfo.duration); ProgramAudio audioType = ProgramAudio.Stereo; @@ -369,6 +360,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings return info; } + private DateTime GetDate(string value) + { + var date = DateTime.ParseExact(value, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", CultureInfo.InvariantCulture); + + if (date.Kind != DateTimeKind.Utc) + { + date = DateTime.SpecifyKind(date, DateTimeKind.Utc); + } + return date; + } + private string GetProgramLogo(string apiUrl, ScheduleDirect.ShowImages images) { string url = ""; @@ -408,7 +410,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings { imageIdString += "\"" + i.Substring(0, 10) + "\","; } - ; }); imageIdString = imageIdString.TrimEnd(',') + "]"; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index d73b144b8..0a33d7383 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -580,7 +580,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.Name = channelInfo.Name; } - await item.RefreshMetadata(new MetadataRefreshOptions + await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = isNew, ReplaceImages = replaceImages.Distinct().ToList() @@ -659,7 +659,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions()); + _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)); return item; } @@ -759,7 +759,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } - _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions()); + _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)); return item.Id; } @@ -1082,6 +1082,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv await CleanDatabaseInternal(newChannelIdList, new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false); await CleanDatabaseInternal(newProgramIdList, new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false); + var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault(); + + if (coreService != null) + { + await coreService.RefreshSeriesTimers(cancellationToken, new Progress<double>()).ConfigureAwait(false); + } + // Load these now which will prefetch metadata var dtoOptions = new DtoOptions(); dtoOptions.Fields.Remove(ItemFields.SyncInfo); @@ -1155,7 +1162,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv foreach (var program in channelPrograms) { + if (program.StartDate.Kind != DateTimeKind.Utc) + { + _logger.Error("{0} returned StartDate.DateTimeKind.{1} instead of UTC for program {2}", service.Name, program.StartDate.Kind.ToString(), program.Name); + } + else if (program.EndDate.Kind != DateTimeKind.Utc) + { + _logger.Error("{0} returned EndDate.DateTimeKind.{1} instead of UTC for program {2}", service.Name, program.EndDate.Kind.ToString(), program.Name); + } + var programItem = await GetProgram(program, channelId, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false); + programs.Add(programItem.Id); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 909e2bba5..e5222e55d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -109,23 +109,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { - // Check to make sure the tuner is available - // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error - if (hostsWithChannel.Count > 1 && !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) + try { - Logger.Error("Tuner is not currently available"); - continue; - } + var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false); - var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false); + // Prefix the id with the host Id so that we can easily find it + foreach (var mediaSource in mediaSources) + { + mediaSource.Id = host.Id + mediaSource.Id; + } - // Prefix the id with the host Id so that we can easily find it - foreach (var mediaSource in mediaSources) + return mediaSources; + } + catch (Exception ex) { - mediaSource.Id = host.Id + mediaSource.Id; + Logger.Error("Error opening tuner", ex); } - - return mediaSources; } } @@ -163,23 +162,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { - // Check to make sure the tuner is available - // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error - // If a streamId is specified then availibility has already been checked in GetChannelStreamMediaSources - if (string.IsNullOrWhiteSpace(streamId) && hostsWithChannel.Count > 1) + try { - if (!await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) + var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + + if (stream != null) { - Logger.Error("Tuner is not currently available"); - continue; + return stream; } } - - var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); - - if (stream != null) + catch (Exception ex) { - return stream; + Logger.Error("Error opening tuner", ex); } } } @@ -187,21 +181,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts throw new LiveTvConflictException(); } - protected async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - { - try - { - return await IsAvailableInternal(tuner, channelId, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error checking tuner availability", ex); - return false; - } - } - - protected abstract Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken); - protected abstract bool IsValidChannelId(string channelId); protected LiveTvOptions GetConfiguration() diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index bccb0db0a..571b00257 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -325,11 +325,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun BitRate = 128000 } }, - RequiresOpening = false, - RequiresClosing = false, + RequiresOpening = true, + RequiresClosing = true, BufferMs = 1000, Container = "ts", - Id = profile + Id = profile, + SupportsDirectPlay = true, + SupportsDirectStream = true, + SupportsTranscoding = true }; return mediaSource; @@ -395,12 +398,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false); } } - - protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - { - var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false); - - return info.Any(i => i.Status == LiveTvTunerStatus.Available || string.Equals(i.ChannelId, channelId, StringComparison.OrdinalIgnoreCase)); - } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 3783e4b08..31139b15d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -12,14 +12,18 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { public class M3UTunerHost : BaseTunerHost, ITunerHost { - public M3UTunerHost(IConfigurationManager config, ILogger logger) + private readonly IFileSystem _fileSystem; + + public M3UTunerHost(IConfigurationManager config, ILogger logger, IFileSystem fileSystem) : base(config, logger) { + _fileSystem = fileSystem; } public override string Type @@ -119,7 +123,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts public async Task Validate(TunerHostInfo info) { - if (!File.Exists(info.Url)) + if (!_fileSystem.FileExists(info.Url)) { throw new FileNotFoundException(); } @@ -190,10 +194,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } return new List<MediaSourceInfo> { }; } - - protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - { - return Task.FromResult(true); - } } } |
