protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var urlHash = info.Url.GetMD5().ToString("N"); var prefix = ChannelIdPrefix + urlHash; if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return null; } var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false); var m3uchannels = channels.Cast<M3UChannel>(); 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<MediaStream> { 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, ReadAtNativeFramerate = true }; return new List<MediaSourceInfo> { mediaSource }; } return new List<MediaSourceInfo>(); }
protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false); var liveStream = new LiveStream(sources.First()); return liveStream; }
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { var options = new HttpRequestOptions { Url = string.Format("{0}/lineup.json", GetApiUrl(info, false)), CancellationToken = cancellationToken }; using (var stream = await _httpClient.Get(options)) { var root = JsonSerializer.DeserializeFromStream<List<Channels>>(stream); if (root != null) { var result = root.Select(i => new ChannelInfo { Name = i.GuideName, Number = i.GuideNumber.ToString(CultureInfo.InvariantCulture), Id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture), IsFavorite = i.Favorite }); if (info.ImportFavoritesOnly) { result = result.Where(i => (i.IsFavorite ?? true)).ToList(); } return result; } return new List<ChannelInfo>(); } }
private async Task<string> GetModelInfo(TunerHostInfo info, CancellationToken cancellationToken) { string model = null; using (var stream = await _httpClient.Get(new HttpRequestOptions() { Url = string.Format("{0}/", GetApiUrl(info, false)), CancellationToken = cancellationToken, CacheLength = TimeSpan.FromDays(1), CacheMode = CacheMode.Unconditional, TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds) })) { using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) { while (!sr.EndOfStream) { string line = StripXML(sr.ReadLine()); if (line.StartsWith("Model:")) { model = line.Replace("Model: ", ""); } //if (line.StartsWith("Device ID:")) { deviceID = line.Replace("Device ID: ", ""); } //if (line.StartsWith("Firmware:")) { firmware = line.Replace("Firmware: ", ""); } } } } return model; }
public async Task Validate(TunerHostInfo info) { using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false)) { } }
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(); Logger.Debug("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list)); if (!string.IsNullOrWhiteSpace(key) && list.Count > 0) { cache = cache ?? new ChannelCache(); cache.Date = DateTime.UtcNow; cache.Channels = list; _channelCache.AddOrUpdate(key, cache, (k, v) => cache); } return list; }
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { var urlHash = info.Url.GetMD5().ToString("N"); // Read the file and display it line by line. using (var reader = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false))) { return GetChannels(reader, urlHash); } }
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken) { if (!string.IsNullOrWhiteSpace(tuner.M3UUrl)) { return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); } var channels = await new ChannelScan(Logger).Scan(tuner, cancellationToken).ConfigureAwait(false); return channels; }
private string GetChannelId(TunerHostInfo info, Channels i) { var id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture); if (info.DataVersion >= 1) { id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N"); } return id; }
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { var url = info.Url; var urlHash = url.GetMD5().ToString("N"); string line; // Read the file and display it line by line. using (var file = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false))) { var channels = new List<M3UChannel>(); string channnelName = null; string channelNumber = null; while ((line = file.ReadLine()) != null) { line = line.Trim(); if (string.IsNullOrWhiteSpace(line)) { continue; } if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) { continue; } if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase)) { var parts = line.Split(new[] { ':' }, 2).Last().Split(new[] { ',' }, 2); channelNumber = parts[0]; channnelName = parts[1]; } else if (!string.IsNullOrWhiteSpace(channelNumber)) { channels.Add(new M3UChannel { Name = channnelName, Number = channelNumber, Id = ChannelIdPrefix + urlHash + channelNumber, Path = line }); channelNumber = null; channnelName = null; } } return channels; } }
private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) { var options = new HttpRequestOptions { Url = string.Format("{0}/lineup.json", GetApiUrl(info, false)), CancellationToken = cancellationToken }; using (var stream = await _httpClient.Get(options)) { var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>(); if (info.ImportFavoritesOnly) { lineup = lineup.Where(i => i.Favorite).ToList(); } return lineup.Where(i => !i.DRM).ToList(); } }
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); }
public async Task Validate(TunerHostInfo info) { if (info.IsEnabled) { await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false); } }
protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { Logger.Info("GetChannelStream: channel id: {0}. stream id: {1}", channelId, streamId ?? string.Empty); if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Channel not found"); } channelId = channelId.Substring(ChannelIdPrefix.Length); return GetMediaSource(info, channelId, streamId); }
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var list = new List<MediaSourceInfo>(); if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase)) { return list; } channelId = channelId.Substring(ChannelIdPrefix.Length); list.Add(GetMediaSource(info, channelId, "native")); try { string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false); model = model ?? string.Empty; if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1) { list.Insert(0, GetMediaSource(info, channelId, "heavy")); list.Add(GetMediaSource(info, channelId, "internet480")); list.Add(GetMediaSource(info, channelId, "internet360")); list.Add(GetMediaSource(info, channelId, "internet240")); list.Add(GetMediaSource(info, channelId, "mobile")); } } catch (Exception ex) { } return list; }
private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, string profile) { int? width = null; int? height = null; bool isInterlaced = true; var videoCodec = !string.IsNullOrWhiteSpace(GetEncodingOptions().HardwareAccelerationType) ? null : "mpeg2video"; int? videoBitrate = null; if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase)) { width = 1280; height = 720; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2000000; } else if (string.Equals(profile, "heavy", StringComparison.OrdinalIgnoreCase)) { width = 1920; height = 1080; isInterlaced = false; videoCodec = "h264"; videoBitrate = 15000000; } else if (string.Equals(profile, "internet720", StringComparison.OrdinalIgnoreCase)) { width = 1280; height = 720; isInterlaced = false; videoCodec = "h264"; videoBitrate = 8000000; } else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase)) { width = 1280; height = 720; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2500000; } else if (string.Equals(profile, "internet480", StringComparison.OrdinalIgnoreCase)) { width = 848; height = 480; isInterlaced = false; videoCodec = "h264"; videoBitrate = 2000000; } else if (string.Equals(profile, "internet360", StringComparison.OrdinalIgnoreCase)) { width = 640; height = 360; isInterlaced = false; videoCodec = "h264"; videoBitrate = 1500000; } else if (string.Equals(profile, "internet240", StringComparison.OrdinalIgnoreCase)) { width = 432; height = 240; isInterlaced = false; videoCodec = "h264"; videoBitrate = 1000000; } var url = GetApiUrl(info, true) + "/auto/v" + channelId; if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) { url += "?transcode=" + profile; } var mediaSource = new MediaSourceInfo { Path = url, Protocol = MediaProtocol.Http, MediaStreams = new List<MediaStream> { 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 = isInterlaced, Codec = videoCodec, Width = width, Height = height, BitRate = videoBitrate }, 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, Codec = "ac3", BitRate = 192000 } }, RequiresOpening = false, RequiresClosing = false, BufferMs = 0, Container = "ts", Id = profile, SupportsDirectPlay = true, SupportsDirectStream = true, SupportsTranscoding = true }; return mediaSource; }
protected abstract Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) { return Task.FromResult(true); }
public async Task Validate(TunerHostInfo info) { if (!info.IsEnabled) { return; } // Test it by pulling down the lineup using (var stream = await _httpClient.Get(new HttpRequestOptions { Url = string.Format("{0}/discover.json", GetApiUrl(info, false)), CancellationToken = CancellationToken.None })) { var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream); info.DeviceId = response.DeviceID; } }
protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) { var updatedInfo = await SatIpDiscovery.Current.GetInfo(tuner.InfoUrl, cancellationToken).ConfigureAwait(false); return updatedInfo.TunersAvailable > 0; }
protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) { var sources = await GetChannelStreamMediaSources(tuner, channelId, cancellationToken).ConfigureAwait(false); return sources.First(); }
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info) { info = (TunerHostInfo)_jsonSerializer.DeserializeFromString(_jsonSerializer.SerializeToString(info), typeof(TunerHostInfo)); var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase)); if (provider == null) { throw new ResourceNotFoundException(); } await provider.Validate(info).ConfigureAwait(false); var config = GetConfiguration(); var index = config.TunerHosts.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase)); if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) { info.Id = Guid.NewGuid().ToString("N"); config.TunerHosts.Add(info); } else { config.TunerHosts[index] = info; } _config.SaveConfiguration("livetv", config); _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>(); return info; }
public Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken) { return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken); }
protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { Logger.Info("GetChannelStream: channel id: {0}. stream id: {1}", channelId, streamId ?? string.Empty); if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Channel not found"); } var hdhrId = GetHdHrIdFromChannelId(channelId); return await GetMediaSource(info, hdhrId, streamId).ConfigureAwait(false); }
public async Task<List<LiveTvTunerInfo>> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) { var model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false); using (var stream = await _httpClient.Get(new HttpRequestOptions() { Url = string.Format("{0}/tuners.html", GetApiUrl(info, false)), CancellationToken = cancellationToken, TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds) })) { var tuners = new List<LiveTvTunerInfo>(); using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8)) { while (!sr.EndOfStream) { string line = StripXML(sr.ReadLine()); if (line.Contains("Channel")) { LiveTvTunerStatus status; var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); var name = line.Substring(0, index - 1); var currentChannel = line.Substring(index + 7); if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; } tuners.Add(new LiveTvTunerInfo { Name = name, SourceType = string.IsNullOrWhiteSpace(model) ? Name : model, ProgramName = currentChannel, Status = status }); } } } return tuners; } }
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false); }
private string GetApiUrl(TunerHostInfo info, bool isPlayback) { var url = info.Url; if (string.IsNullOrWhiteSpace(url)) { throw new ArgumentException("Invalid tuner info"); } if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { url = "http://" + url; } var uri = new Uri(url); if (isPlayback) { var builder = new UriBuilder(uri); builder.Port = 5004; uri = builder.Uri; } return uri.AbsoluteUri.TrimEnd('/'); }
public List<LiveTvTunerInfo> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) { var list = new List<LiveTvTunerInfo>(); for (var i = 0; i < info.Tuners; i++) { list.Add(new LiveTvTunerInfo { Name = info.FriendlyName ?? Name, SourceType = Type, Status = LiveTvTunerStatus.Available, Id = info.Url.GetMD5().ToString("N") + i.ToString(CultureInfo.InvariantCulture), Url = info.Url }); } return list; }
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { var lineup = await GetLineup(info, cancellationToken).ConfigureAwait(false); return lineup.Select(i => new ChannelInfo { Name = i.GuideName, Number = i.GuideNumber.ToString(CultureInfo.InvariantCulture), Id = GetChannelId(info, i), IsFavorite = i.Favorite, TunerHostId = info.Id, IsHD = i.HD == 1, AudioCodec = i.AudioCodec, VideoCodec = i.VideoCodec }); }