aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs')
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs218
1 files changed, 218 insertions, 0 deletions
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
new file mode 100644
index 000000000..909e2bba5
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -0,0 +1,218 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+{
+ public abstract class BaseTunerHost
+ {
+ protected readonly IConfigurationManager Config;
+ protected readonly ILogger Logger;
+
+ private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
+ new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
+
+ public BaseTunerHost(IConfigurationManager config, ILogger logger)
+ {
+ Config = config;
+ Logger = logger;
+ }
+
+ protected abstract Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
+ public abstract string Type { get; }
+
+ public async Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
+ {
+ ChannelCache cache = null;
+ var key = tuner.Id;
+
+ if (enableCache && !string.IsNullOrWhiteSpace(key) && _channelCache.TryGetValue(key, out cache))
+ {
+ if ((DateTime.UtcNow - cache.Date) < TimeSpan.FromMinutes(60))
+ {
+ return cache.Channels.ToList();
+ }
+ }
+
+ var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
+ var list = result.ToList();
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ cache = cache ?? new ChannelCache();
+ cache.Date = DateTime.UtcNow;
+ cache.Channels = list;
+ _channelCache.AddOrUpdate(key, cache, (k, v) => cache);
+ }
+
+ return list;
+ }
+
+ private List<TunerHostInfo> GetTunerHosts()
+ {
+ return GetConfiguration().TunerHosts
+ .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+
+ public async Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken)
+ {
+ var list = new List<ChannelInfo>();
+
+ var hosts = GetTunerHosts();
+
+ foreach (var host in hosts)
+ {
+ try
+ {
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+ var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
+
+ list.AddRange(newChannels);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting channel list", ex);
+ }
+ }
+
+ return list;
+ }
+
+ protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
+
+ public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
+ {
+ if (IsValidChannelId(channelId))
+ {
+ var hosts = GetTunerHosts();
+
+ var hostsWithChannel = new List<TunerHostInfo>();
+
+ foreach (var host in hosts)
+ {
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+
+ if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
+ {
+ hostsWithChannel.Add(host);
+ }
+ }
+
+ 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))
+ {
+ Logger.Error("Tuner is not currently available");
+ continue;
+ }
+
+ 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;
+ }
+
+ return mediaSources;
+ }
+ }
+
+ return new List<MediaSourceInfo>();
+ }
+
+ protected abstract Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
+
+ public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+ {
+ if (IsValidChannelId(channelId))
+ {
+ var hosts = GetTunerHosts();
+
+ var hostsWithChannel = new List<TunerHostInfo>();
+
+ foreach (var host in hosts)
+ {
+ if (string.IsNullOrWhiteSpace(streamId))
+ {
+ var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+
+ if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
+ {
+ hostsWithChannel.Add(host);
+ }
+ }
+ else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
+ {
+ hostsWithChannel = new List<TunerHostInfo> { host };
+ streamId = streamId.Substring(host.Id.Length);
+ break;
+ }
+ }
+
+ 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)
+ {
+ if (!await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
+ {
+ Logger.Error("Tuner is not currently available");
+ continue;
+ }
+ }
+
+ var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
+
+ if (stream != null)
+ {
+ return stream;
+ }
+ }
+ }
+
+ 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()
+ {
+ return Config.GetConfiguration<LiveTvOptions>("livetv");
+ }
+
+ private class ChannelCache
+ {
+ public DateTime Date;
+ public List<ChannelInfo> Channels;
+ }
+ }
+}