diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs index f6db2f5a8d..6781e498ac 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs @@ -21,225 +21,225 @@ using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { - //public class SatIpDiscovery : IServerEntryPoint - //{ - // private readonly IDeviceDiscovery _deviceDiscovery; - // private readonly IServerConfigurationManager _config; - // private readonly ILogger _logger; - // private readonly ILiveTvManager _liveTvManager; - // private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - // private readonly IHttpClient _httpClient; - // private readonly IJsonSerializer _json; - - // public static SatIpDiscovery Current; - - // private readonly List _discoveredHosts = new List(); - - // public List DiscoveredHosts - // { - // get { return _discoveredHosts.ToList(); } - // } - - // public SatIpDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient, IJsonSerializer json) - // { - // _deviceDiscovery = deviceDiscovery; - // _config = config; - // _logger = logger; - // _liveTvManager = liveTvManager; - // _httpClient = httpClient; - // _json = json; - // Current = this; - // } - - // public void Run() - // { - // _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; - // } - - // void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) - // { - // string st = null; - // string nt = null; - // e.Headers.TryGetValue("ST", out st); - // e.Headers.TryGetValue("NT", out nt); - - // if (string.Equals(st, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase) || - // string.Equals(nt, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase)) - // { - // string location; - // if (e.Headers.TryGetValue("Location", out location) && !string.IsNullOrWhiteSpace(location)) - // { - // _logger.Debug("SAT IP found at {0}", location); - - // // Just get the beginning of the url - // AddDevice(location); - // } - // } - // } - - // private async void AddDevice(string location) - // { - // await _semaphore.WaitAsync().ConfigureAwait(false); - - // try - // { - // if (_discoveredHosts.Any(i => string.Equals(i.Type, SatIpHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(location, i.Url, StringComparison.OrdinalIgnoreCase))) - // { - // return; - // } - - // _logger.Debug("Will attempt to add SAT device {0}", location); - // var info = await GetInfo(location, CancellationToken.None).ConfigureAwait(false); - - // _discoveredHosts.Add(info); - // } - // catch (OperationCanceledException) - // { - - // } - // catch (NotImplementedException) - // { - - // } - // catch (Exception ex) - // { - // _logger.ErrorException("Error saving device", ex); - // } - // finally - // { - // _semaphore.Release(); - // } - // } - - // public void Dispose() - // { - // } - - // public async Task GetInfo(string url, CancellationToken cancellationToken) - // { - // var result = new SatIpTunerHostInfo - // { - // Url = url, - // IsEnabled = true, - // Type = SatIpHost.DeviceType, - // Tuners = 1, - // TunersAvailable = 1 - // }; - - // using (var stream = await _httpClient.Get(url, cancellationToken).ConfigureAwait(false)) - // { - // using (var streamReader = new StreamReader(stream)) - // { - // // Use XmlReader for best performance - // using (var reader = XmlReader.Create(streamReader)) - // { - // reader.MoveToContent(); - - // // Loop through each element - // while (reader.Read()) - // { - // if (reader.NodeType == XmlNodeType.Element) - // { - // switch (reader.Name) - // { - // case "device": - // using (var subtree = reader.ReadSubtree()) - // { - // FillFromDeviceNode(result, subtree); - // } - // break; - // default: - // reader.Skip(); - // break; - // } - // } - // } - // } - // } - // } - - // if (string.IsNullOrWhiteSpace(result.Id)) - // { - // throw new NotImplementedException(); - // } - - // // Device hasn't implemented an m3u list - // if (string.IsNullOrWhiteSpace(result.M3UUrl)) - // { - // result.IsEnabled = false; - // } - - // else if (!result.M3UUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - // { - // var fullM3uUrl = url.Substring(0, url.LastIndexOf('/')); - // result.M3UUrl = fullM3uUrl + "/" + result.M3UUrl.TrimStart('/'); - // } - - // _logger.Debug("SAT device result: {0}", _json.SerializeToString(result)); - - // return result; - // } - - // private void FillFromDeviceNode(SatIpTunerHostInfo info, XmlReader reader) - // { - // reader.MoveToContent(); - - // while (reader.Read()) - // { - // if (reader.NodeType == XmlNodeType.Element) - // { - // switch (reader.LocalName) - // { - // case "UDN": - // { - // info.Id = reader.ReadElementContentAsString(); - // break; - // } - - // case "friendlyName": - // { - // info.FriendlyName = reader.ReadElementContentAsString(); - // break; - // } - - // case "satip:X_SATIPCAP": - // case "X_SATIPCAP": - // { - // // DVBS2-2 - // var value = reader.ReadElementContentAsString() ?? string.Empty; - // var parts = value.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries); - // if (parts.Length == 2) - // { - // int intValue; - // if (int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out intValue)) - // { - // info.TunersAvailable = intValue; - // } - - // if (int.TryParse(parts[0].Substring(parts[0].Length - 1), NumberStyles.Any, CultureInfo.InvariantCulture, out intValue)) - // { - // info.Tuners = intValue; - // } - // } - // break; - // } - - // case "satip:X_SATIPM3U": - // case "X_SATIPM3U": - // { - // // /channellist.lua?select=m3u - // info.M3UUrl = reader.ReadElementContentAsString(); - // break; - // } - - // default: - // reader.Skip(); - // break; - // } - // } - // } - // } - //} + public class SatIpDiscovery : IServerEntryPoint + { + private readonly IDeviceDiscovery _deviceDiscovery; + private readonly IServerConfigurationManager _config; + private readonly ILogger _logger; + private readonly ILiveTvManager _liveTvManager; + private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); + private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _json; + + public static SatIpDiscovery Current; + + private readonly List _discoveredHosts = new List(); + + public List DiscoveredHosts + { + get { return _discoveredHosts.ToList(); } + } + + public SatIpDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient, IJsonSerializer json) + { + _deviceDiscovery = deviceDiscovery; + _config = config; + _logger = logger; + _liveTvManager = liveTvManager; + _httpClient = httpClient; + _json = json; + Current = this; + } + + public void Run() + { + _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; + } + + void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) + { + string st = null; + string nt = null; + e.Headers.TryGetValue("ST", out st); + e.Headers.TryGetValue("NT", out nt); + + if (string.Equals(st, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase) || + string.Equals(nt, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase)) + { + string location; + if (e.Headers.TryGetValue("Location", out location) && !string.IsNullOrWhiteSpace(location)) + { + _logger.Debug("SAT IP found at {0}", location); + + // Just get the beginning of the url + AddDevice(location); + } + } + } + + private async void AddDevice(string location) + { + await _semaphore.WaitAsync().ConfigureAwait(false); + + try + { + if (_discoveredHosts.Any(i => string.Equals(i.Type, SatIpHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(location, i.Url, StringComparison.OrdinalIgnoreCase))) + { + return; + } + + _logger.Debug("Will attempt to add SAT device {0}", location); + var info = await GetInfo(location, CancellationToken.None).ConfigureAwait(false); + + _discoveredHosts.Add(info); + } + catch (OperationCanceledException) + { + + } + catch (NotImplementedException) + { + + } + catch (Exception ex) + { + _logger.ErrorException("Error saving device", ex); + } + finally + { + _semaphore.Release(); + } + } + + public void Dispose() + { + } + + public async Task GetInfo(string url, CancellationToken cancellationToken) + { + var result = new SatIpTunerHostInfo + { + Url = url, + IsEnabled = true, + Type = SatIpHost.DeviceType, + Tuners = 1, + TunersAvailable = 1 + }; + + using (var stream = await _httpClient.Get(url, cancellationToken).ConfigureAwait(false)) + { + using (var streamReader = new StreamReader(stream)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader)) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "device": + using (var subtree = reader.ReadSubtree()) + { + FillFromDeviceNode(result, subtree); + } + break; + default: + reader.Skip(); + break; + } + } + } + } + } + } + + if (string.IsNullOrWhiteSpace(result.Id)) + { + throw new NotImplementedException(); + } + + // Device hasn't implemented an m3u list + if (string.IsNullOrWhiteSpace(result.M3UUrl)) + { + result.IsEnabled = false; + } + + else if (!result.M3UUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + var fullM3uUrl = url.Substring(0, url.LastIndexOf('/')); + result.M3UUrl = fullM3uUrl + "/" + result.M3UUrl.TrimStart('/'); + } + + _logger.Debug("SAT device result: {0}", _json.SerializeToString(result)); + + return result; + } + + private void FillFromDeviceNode(SatIpTunerHostInfo info, XmlReader reader) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "UDN": + { + info.Id = reader.ReadElementContentAsString(); + break; + } + + case "friendlyName": + { + info.FriendlyName = reader.ReadElementContentAsString(); + break; + } + + case "satip:X_SATIPCAP": + case "X_SATIPCAP": + { + // DVBS2-2 + var value = reader.ReadElementContentAsString() ?? string.Empty; + var parts = value.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2) + { + int intValue; + if (int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out intValue)) + { + info.TunersAvailable = intValue; + } + + if (int.TryParse(parts[0].Substring(parts[0].Length - 1), NumberStyles.Any, CultureInfo.InvariantCulture, out intValue)) + { + info.Tuners = intValue; + } + } + break; + } + + case "satip:X_SATIPM3U": + case "X_SATIPM3U": + { + // /channellist.lua?select=m3u + info.M3UUrl = reader.ReadElementContentAsString(); + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + } public class SatIpTunerHostInfo : TunerHostInfo { diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs index 11213be232..d305a886a6 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -19,153 +19,153 @@ using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { - //public class SatIpHost : BaseTunerHost, ITunerHost - //{ - // private readonly IFileSystem _fileSystem; - // private readonly IHttpClient _httpClient; - - // public SatIpHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) - // : base(config, logger, jsonSerializer, mediaEncoder) - // { - // _fileSystem = fileSystem; - // _httpClient = httpClient; - // } - - // private const string ChannelIdPrefix = "sat_"; - - // protected override async Task> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken) - // { - // var satInfo = (SatIpTunerHostInfo) tuner; - - // return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(satInfo.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); - // } - - // public static string DeviceType - // { - // get { return "satip"; } - // } - - // public override string Type - // { - // get { return DeviceType; } - // } - - // protected override async Task> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - // { - // var urlHash = tuner.Url.GetMD5().ToString("N"); - // var prefix = ChannelIdPrefix + urlHash; - // if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - // { - // return null; - // } - - // var channels = await GetChannels(tuner, true, cancellationToken).ConfigureAwait(false); - // var m3uchannels = channels.Cast(); - // var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase)); - // if (channel != null) - // { - // var path = channel.Path; - // MediaProtocol protocol = MediaProtocol.File; - // if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - // { - // protocol = MediaProtocol.Http; - // } - // else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase)) - // { - // protocol = MediaProtocol.Rtmp; - // } - // else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase)) - // { - // protocol = MediaProtocol.Rtsp; - // } - - // var mediaSource = new MediaSourceInfo - // { - // Path = channel.Path, - // Protocol = protocol, - // MediaStreams = new List - // { - // new MediaStream - // { - // Type = MediaStreamType.Video, - // // Set the index to -1 because we don't know the exact index of the video stream within the container - // Index = -1, - // IsInterlaced = true - // }, - // new MediaStream - // { - // Type = MediaStreamType.Audio, - // // Set the index to -1 because we don't know the exact index of the audio stream within the container - // Index = -1 - - // } - // }, - // RequiresOpening = false, - // RequiresClosing = false - // }; - - // return new List { mediaSource }; - // } - // return new List { }; - // } - - // protected override async Task GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) - // { - // var sources = await GetChannelStreamMediaSources(tuner, channelId, cancellationToken).ConfigureAwait(false); - - // return sources.First(); - // } - - // protected override async Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - // { - // var updatedInfo = await SatIpDiscovery.Current.GetInfo(tuner.Url, cancellationToken).ConfigureAwait(false); - - // return updatedInfo.TunersAvailable > 0; - // } - - // protected override bool IsValidChannelId(string channelId) - // { - // return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); - // } - - // protected override List GetTunerHosts() - // { - // return SatIpDiscovery.Current.DiscoveredHosts; - // } - - // public string Name - // { - // get { return "Sat IP"; } - // } - - // public Task> GetTunerInfos(CancellationToken cancellationToken) - // { - // var list = GetTunerHosts() - // .SelectMany(i => GetTunerInfos(i, cancellationToken)) - // .ToList(); - - // return Task.FromResult(list); - // } - - // public List GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) - // { - // var satInfo = (SatIpTunerHostInfo) info; - - // var list = new List(); - - // for (var i = 0; i < satInfo.Tuners; i++) - // { - // list.Add(new LiveTvTunerInfo - // { - // Name = satInfo.FriendlyName ?? Name, - // SourceType = Type, - // Status = LiveTvTunerStatus.Available, - // Id = info.Url.GetMD5().ToString("N") + i.ToString(CultureInfo.InvariantCulture), - // Url = info.Url - // }); - // } - - // return list; - // } - //} + public class SatIpHost : BaseTunerHost, ITunerHost + { + private readonly IFileSystem _fileSystem; + private readonly IHttpClient _httpClient; + + public SatIpHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + : base(config, logger, jsonSerializer, mediaEncoder) + { + _fileSystem = fileSystem; + _httpClient = httpClient; + } + + private const string ChannelIdPrefix = "sat_"; + + protected override async Task> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken) + { + var satInfo = (SatIpTunerHostInfo)tuner; + + return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(satInfo.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); + } + + public static string DeviceType + { + get { return "satip"; } + } + + public override string Type + { + get { return DeviceType; } + } + + protected override async Task> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + var urlHash = tuner.Url.GetMD5().ToString("N"); + var prefix = ChannelIdPrefix + urlHash; + if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var channels = await GetChannels(tuner, true, cancellationToken).ConfigureAwait(false); + var m3uchannels = channels.Cast(); + var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase)); + if (channel != null) + { + var path = channel.Path; + MediaProtocol protocol = MediaProtocol.File; + if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Http; + } + else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Rtmp; + } + else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase)) + { + protocol = MediaProtocol.Rtsp; + } + + var mediaSource = new MediaSourceInfo + { + Path = channel.Path, + Protocol = protocol, + MediaStreams = new List + { + new MediaStream + { + Type = MediaStreamType.Video, + // Set the index to -1 because we don't know the exact index of the video stream within the container + Index = -1, + IsInterlaced = true + }, + new MediaStream + { + Type = MediaStreamType.Audio, + // Set the index to -1 because we don't know the exact index of the audio stream within the container + Index = -1 + + } + }, + RequiresOpening = false, + RequiresClosing = false + }; + + return new List { mediaSource }; + } + return new List { }; + } + + protected override async Task GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) + { + var sources = await GetChannelStreamMediaSources(tuner, channelId, cancellationToken).ConfigureAwait(false); + + return sources.First(); + } + + protected override async Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) + { + var updatedInfo = await SatIpDiscovery.Current.GetInfo(tuner.Url, cancellationToken).ConfigureAwait(false); + + return updatedInfo.TunersAvailable > 0; + } + + protected override bool IsValidChannelId(string channelId) + { + return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); + } + + protected override List GetTunerHosts() + { + return SatIpDiscovery.Current.DiscoveredHosts; + } + + public string Name + { + get { return "Sat IP"; } + } + + public Task> GetTunerInfos(CancellationToken cancellationToken) + { + var list = GetTunerHosts() + .SelectMany(i => GetTunerInfos(i, cancellationToken)) + .ToList(); + + return Task.FromResult(list); + } + + public List GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) + { + var satInfo = (SatIpTunerHostInfo)info; + + var list = new List(); + + for (var i = 0; i < satInfo.Tuners; i++) + { + list.Add(new LiveTvTunerInfo + { + Name = satInfo.FriendlyName ?? Name, + SourceType = Type, + Status = LiveTvTunerStatus.Available, + Id = info.Url.GetMD5().ToString("N") + i.ToString(CultureInfo.InvariantCulture), + Url = info.Url + }); + } + + return list; + } + } }