Luke Pulverenti 10 лет назад
Родитель
Сommit
ba9ed26c4a

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -924,7 +924,7 @@ namespace MediaBrowser.Api.Playback
                 state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
             }
 
-            if (state.MediaSource.RequiresOpening)
+            if (state.MediaSource.RequiresOpening ?? false)
             {
                 var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
                 {

+ 1 - 1
MediaBrowser.Api/Playback/StreamState.cs

@@ -185,7 +185,7 @@ namespace MediaBrowser.Api.Playback
 
         private async void DisposeLiveStream()
         {
-            if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Request.LiveStreamId))
+            if ((MediaSource.RequiresClosing ?? false) && string.IsNullOrWhiteSpace(Request.LiveStreamId))
             {
                 try
                 {

+ 8 - 0
MediaBrowser.Controller/LiveTv/ITunerHost.cs

@@ -47,6 +47,14 @@ namespace MediaBrowser.Controller.LiveTv
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
         Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken);
         /// <summary>
+        /// Gets the channel stream media sources.
+        /// </summary>
+        /// <param name="info">The information.</param>
+        /// <param name="channelId">The channel identifier.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
+        Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken);
+        /// <summary>
         /// Validates the specified information.
         /// </summary>
         /// <param name="info">The information.</param>

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -436,7 +436,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false);
             }
 
-            if (state.MediaSource.RequiresOpening)
+            if (state.MediaSource.RequiresOpening ?? false)
             {
                 var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
                 {

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs

@@ -137,7 +137,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
         private async void DisposeLiveStream()
         {
-            if (MediaSource.RequiresClosing)
+            if (MediaSource.RequiresClosing ?? false)
             {
                 try
                 {

+ 2 - 2
MediaBrowser.Model/Dto/MediaSourceInfo.cs

@@ -26,9 +26,9 @@ namespace MediaBrowser.Model.Dto
         public bool SupportsDirectStream { get; set; }
         public bool SupportsDirectPlay { get; set; }
 
-        public bool RequiresOpening { get; set; }
+        public bool? RequiresOpening { get; set; }
         public string OpenToken { get; set; }
-        public bool RequiresClosing { get; set; }
+        public bool? RequiresClosing { get; set; }
         public string LiveStreamId { get; set; }
         public int? BufferMs { get; set; }
 

+ 1 - 0
MediaBrowser.Model/LiveTv/LiveTvOptions.cs

@@ -34,5 +34,6 @@ namespace MediaBrowser.Model.LiveTv
         public string Password { get; set; }
         public string ListingsId { get; set; }
         public string ZipCode { get; set; }
+        public string Country { get; set; }
     }
 }

+ 1 - 1
MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs

@@ -439,7 +439,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 LiveStreamInfo current;
                 if (_openStreams.TryGetValue(id, out current))
                 {
-                    if (current.MediaSource.RequiresClosing)
+                    if (current.MediaSource.RequiresClosing ?? false)
                     {
                         var tuple = GetProvider(id);
 

+ 105 - 25
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1,5 +1,6 @@
 using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.LiveTv;
@@ -74,20 +75,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             var status = new LiveTvServiceStatusInfo();
             var list = new List<LiveTvTunerInfo>();
 
-            foreach (var host in _liveTvManager.TunerHosts)
+            foreach (var hostInstance in GetTunerHosts())
             {
-                foreach (var hostInstance in host.GetTunerHosts())
+                try
                 {
-                    try
-                    {
-                        var tuners = await host.GetTunerInfos(hostInstance, cancellationToken).ConfigureAwait(false);
+                    var tuners = await hostInstance.Item1.GetTunerInfos(hostInstance.Item2, cancellationToken).ConfigureAwait(false);
 
-                        list.AddRange(tuners);
-                    }
-                    catch (Exception ex)
-                    {
-                        _logger.ErrorException("Error getting tuners", ex);
-                    }
+                    list.AddRange(tuners);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error getting tuners", ex);
                 }
             }
 
@@ -102,20 +100,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         {
             var list = new List<ChannelInfo>();
 
-            foreach (var host in _liveTvManager.TunerHosts)
+            foreach (var hostInstance in GetTunerHosts())
             {
-                foreach (var hostInstance in host.GetTunerHosts())
+                try
                 {
-                    try
-                    {
-                        var channels = await host.GetChannels(hostInstance, cancellationToken).ConfigureAwait(false);
+                    var channels = await hostInstance.Item1.GetChannels(hostInstance.Item2, cancellationToken).ConfigureAwait(false);
+                    var newChannels = channels.ToList();
 
-                        list.AddRange(channels);
-                    }
-                    catch (Exception ex)
+                    foreach (var channel in newChannels)
                     {
-                        _logger.ErrorException("Error getting channels", ex);
+                        channel.Id = hostInstance.Item1.Type.GetMD5().ToString("N") + "-" + channel.Id;
                     }
+
+                    list.AddRange(newChannels);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error getting channels", ex);
                 }
             }
 
@@ -130,6 +131,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             return list;
         }
 
+        private List<Tuple<ITunerHost, TunerHostInfo>> GetTunerHosts()
+        {
+            return GetConfiguration().TunerHosts
+                .Select(i =>
+                {
+                    var provider = _liveTvManager.TunerHosts.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase));
+
+                    return provider == null ? null : new Tuple<ITunerHost, TunerHostInfo>(provider, i);
+                })
+                .Where(i => i != null)
+                .ToList();
+        }
+
         public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
         {
             var remove = _seriesTimerProvider.GetAll().SingleOrDefault(r => r.Id == timerId);
@@ -255,14 +269,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             return Task.FromResult((IEnumerable<SeriesTimerInfo>)_seriesTimerProvider.GetAll());
         }
 
+        private string GetOriginalChannelId(string channelId)
+        {
+            var parts = channelId.Split('-');
+
+            return string.Join("-", parts.Skip(1).ToArray());
+        }
+
         public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
         {
             foreach (var provider in GetListingProviders())
             {
-                var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channelId, startDateUtc, endDateUtc, cancellationToken)
+                var programs = await provider.Item1.GetProgramsAsync(provider.Item2, GetOriginalChannelId(channelId), startDateUtc, endDateUtc, cancellationToken)
                         .ConfigureAwait(false);
                 var list = programs.ToList();
 
+                // Replace the value that came from the provider with a normalized value
+                foreach (var program in list)
+                {
+                    program.ChannelId = channelId;
+                }
+
                 if (list.Count > 0)
                 {
                     return list;
@@ -290,14 +317,67 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             throw new NotImplementedException();
         }
 
-        public Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+        public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
         {
-            throw new NotImplementedException();
+            _logger.Info("Streaming Channel " + channelId);
+
+            var configurationId = channelId.Split('-')[0];
+
+            foreach (var hostInstance in GetTunerHosts())
+            {
+                if (!string.Equals(configurationId, hostInstance.Item1.Type.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase))
+                {
+                    continue;
+                }
+
+                if (!string.IsNullOrWhiteSpace(streamId))
+                {
+                    var originalStreamId = string.Join("-", streamId.Split('-').Skip(1).ToArray());
+
+                    if (!string.Equals(hostInstance.Item2.Id, originalStreamId, StringComparison.OrdinalIgnoreCase))
+                    {
+                        continue;
+                    }
+                }
+
+                MediaSourceInfo mediaSourceInfo = null;
+                try
+                {
+                    mediaSourceInfo = await hostInstance.Item1.GetChannelStream(hostInstance.Item2, GetOriginalChannelId(channelId), streamId, cancellationToken).ConfigureAwait(false);
+                }
+                catch (ApplicationException e)
+                {
+                    _logger.Info(e.Message);
+                    continue;
+                }
+
+                mediaSourceInfo.Id = Guid.NewGuid().ToString("N");
+                return mediaSourceInfo;
+            }
+
+            throw new ApplicationException("Tuner not found.");
         }
 
-        public Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
+        public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
         {
-            throw new NotImplementedException();
+            var configurationId = channelId.Split('-')[0];
+
+            foreach (var hostInstance in GetTunerHosts())
+            {
+                if (string.Equals(configurationId, hostInstance.Item1.Type.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase))
+                {
+                    var sources = await hostInstance.Item1.GetChannelStreamMediaSources(hostInstance.Item2, GetOriginalChannelId(channelId), cancellationToken).ConfigureAwait(false);
+
+                    foreach (var source in sources)
+                    {
+                        source.Id = hostInstance.Item2.Id + "-" + source.Id;
+                    }
+
+                    return sources;
+                }
+            }
+
+            throw new ApplicationException("Tuner not found.");
         }
 
         public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)

+ 4 - 1
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs

@@ -68,9 +68,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
         private void UpdateList(List<T> newList)
         {
+            var file = _dataPath + ".json";
+            Directory.CreateDirectory(Path.GetDirectoryName(file));
+
             lock (_fileDataLock)
             {
-                _jsonSerializer.SerializeToFile(newList, _dataPath + ".json");
+                _jsonSerializer.SerializeToFile(newList, file);
                 _items = newList;
             }
         }

+ 40 - 11
MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -218,7 +218,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
         private ProgramInfo GetProgram(string channel, ScheduleDirect.Program programInfo,
             ScheduleDirect.ProgramDetails details)
         {
-            _logger.Debug("Show type is: " + (details.showType ?? "No ShowType"));
+            //_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 endAt = startAt.AddSeconds(programInfo.duration);
@@ -401,7 +401,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
             _logger.Info("Headends on account ");
 
             var countryParam = string.Equals("ca", country, StringComparison.OrdinalIgnoreCase)
-                ? "Canada"
+                ? "can"
                 : "USA";
 
             var options = new HttpRequestOptions()
@@ -562,6 +562,44 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
             get { return "SchedulesDirect"; }
         }
 
+        private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
+        {
+            var token = await GetToken(info, cancellationToken);
+
+            _logger.Info("Headends on account ");
+
+            var options = new HttpRequestOptions()
+            {
+                Url = ApiUrl + "/lineups",
+                UserAgent = UserAgent,
+                CancellationToken = cancellationToken
+            };
+
+            options.RequestHeaders["token"] = token;
+
+            using (Stream responce = await _httpClient.Get(options).ConfigureAwait(false))
+            {
+                var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(responce);
+
+                return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+            }
+        }
+
+        public async Task Validate(ListingsProviderInfo info)
+        {
+            var hasLineup = await HasLineup(info, CancellationToken.None).ConfigureAwait(false);
+
+            if (!hasLineup)
+            {
+                await AddLineupToAccount(info, CancellationToken.None).ConfigureAwait(false);
+            }
+        }
+
+        public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
+        {
+            return GetHeadends(info, country, location, CancellationToken.None);
+        }
+
         public class ScheduleDirect
         {
             public class Token
@@ -841,14 +879,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
 
         }
 
-        public async Task Validate(ListingsProviderInfo info)
-        {
-            //await AddLineupToAccount(info, CancellationToken.None).ConfigureAwait(false);
-        }
-
-        public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
-        {
-            return GetHeadends(info, country, location, CancellationToken.None);
-        }
     }
 }

+ 2 - 2
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -367,7 +367,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                     info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
                     info.RequiresClosing = true;
 
-                    if (info.RequiresClosing)
+                    if (info.RequiresClosing ?? false)
                     {
                         var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
 
@@ -384,7 +384,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                     info = await service.GetRecordingStream(recording.ExternalId, null, cancellationToken).ConfigureAwait(false);
                     info.RequiresClosing = true;
 
-                    if (info.RequiresClosing)
+                    if (info.RequiresClosing ?? false)
                     {
                         var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
 

+ 16 - 8
MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -53,7 +53,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
         // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
         private const char StreamIdDelimeter = '_';
-        private const string StreamIdDelimeterString = "|";
+        private const string StreamIdDelimeterString = "_";
         
         private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(ILiveTvItem item, CancellationToken cancellationToken)
         {
@@ -86,14 +86,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             foreach (var source in list)
             {
                 source.Type = MediaSourceType.Default;
-                source.RequiresOpening = true;
-                source.BufferMs = source.BufferMs ?? 1500;
 
-                var openKeys = new List<string>();
-                openKeys.Add(item.GetType().Name);
-                openKeys.Add(item.Id.ToString("N"));
-                openKeys.Add(source.Id ?? string.Empty);
-                source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
+                if (!source.RequiresOpening.HasValue)
+                {
+                    source.RequiresOpening = true;
+                }
+
+                if (source.RequiresOpening.HasValue && source.RequiresOpening.Value)
+                {
+                    var openKeys = new List<string>();
+                    openKeys.Add(item.GetType().Name);
+                    openKeys.Add(item.Id.ToString("N"));
+                    openKeys.Add(source.Id ?? string.Empty);
+                    source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
+                }
+
+                source.BufferMs = source.BufferMs ?? 1500;
 
                 // Dummy this up so that direct play checks can still run
                 if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http)

+ 5 - 7
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs

@@ -72,6 +72,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     return;
                 }
 
+                // Strip off the port
+                url = new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped);
+
                 await _liveTvManager.SaveTunerHost(new TunerHostInfo
                 {
                     Type = HdHomerunHost.DeviceType,
@@ -103,13 +106,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             url = url.TrimEnd('/');
 
-            // If there isn't a port, add the default port of 80
-            if (url.Split(':').Length < 3)
-            {
-                url += ":80";
-            }
-
-            return url;
+            // Strip off the port
+            return new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped);
         }
 
         private LiveTvOptions GetConfiguration()

+ 38 - 24
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -51,7 +51,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
             var options = new HttpRequestOptions
             {
-                Url = string.Format("{0}/lineup.json", GetApiUrl(info)),
+                Url = string.Format("{0}/lineup.json", GetApiUrl(info, false)),
                 CancellationToken = cancellationToken
             };
             using (var stream = await _httpClient.Get(options))
@@ -79,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             using (var stream = await _httpClient.Get(new HttpRequestOptions()
             {
-                Url = string.Format("{0}/", GetApiUrl(info)),
+                Url = string.Format("{0}/", GetApiUrl(info, false)),
                 CancellationToken = cancellationToken
             }))
             {
@@ -97,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             using (var stream = await _httpClient.Get(new HttpRequestOptions()
             {
-                Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
+                Url = string.Format("{0}/tuners.html", GetApiUrl(info, false)),
                 CancellationToken = cancellationToken
             }))
             {
@@ -128,7 +128,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             }
         }
 
-        public string GetApiUrl(TunerHostInfo info)
+        private string GetApiUrl(TunerHostInfo info, bool isPlayback)
         {
             var url = info.Url;
 
@@ -137,7 +137,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 url = "http://" + url;
             }
 
-            return url.TrimEnd('/');
+            var uri = new Uri(url);
+
+            if (isPlayback)
+            {
+                var builder = new UriBuilder(uri);
+                builder.Port = 5004;
+                uri = builder.Uri;
+            }
+
+            return uri.AbsoluteUri.TrimEnd('/');
         }
 
         private static string StripXML(string source)
@@ -187,21 +196,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return GetConfiguration().TunerHosts.Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)).ToList();
         }
 
-        public async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+        private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, string profile)
         {
-            var channels = await GetChannels(info, cancellationToken).ConfigureAwait(false);
-            var tuners = await GetTunerInfos(info, cancellationToken).ConfigureAwait(false);
-
-            var channel = channels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
-            if (channel != null)
+            var mediaSource = new MediaSourceInfo
             {
-                if (tuners.FindIndex(t => t.Status == LiveTvTunerStatus.Available) >= 0)
-                {
-                    return new MediaSourceInfo
-                    {
-                        Path = GetApiUrl(info) + "/auto/v" + channelId,
-                        Protocol = MediaProtocol.Http,
-                        MediaStreams = new List<MediaStream>
+                Path = GetApiUrl(info, true) + "/auto/v" + channelId,
+                Protocol = MediaProtocol.Http,
+                MediaStreams = new List<MediaStream>
                         {
                             new MediaStream
                             {
@@ -217,15 +218,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                                 Index = -1
 
                             }
-                        }
-                    };
-                }
+                        },
+                RequiresOpening = false,
+                RequiresClosing = false,
+                BufferMs = 1000
+            };
 
-                throw new ApplicationException("No tuners avaliable.");
-            } 
-            throw new ApplicationException("Channel not found.");
+            return mediaSource;
         }
 
+        public Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+        {
+            var list = new List<MediaSourceInfo>();
+
+            list.Add(GetMediaSource(info, channelId, null));
+
+            return Task.FromResult(list);
+        }
+
+        public async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+        {
+            return GetMediaSource(info, channelId, null);
+        }
 
         public async Task Validate(TunerHostInfo info)
         {

+ 9 - 1
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -171,7 +171,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                             Index = -1
 
                         }
-                    }
+                    },
+                    RequiresOpening = false,
+                    RequiresClosing = false
                 };
             }
             throw new ApplicationException("Host doesnt provide this channel");
@@ -193,5 +195,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                 throw new FileNotFoundException();
             }
         }
+
+
+        public Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 1 - 1
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -1489,5 +1489,5 @@
     "GuideProviderListingsStep": "Step 2: Select Listings",
     "GuideProviderLoginStep": "Step 1: Login",
     "LabelLineup": "Lineup",
-    "MessageTunerDeviceNotListed": "Is your tuner device not listed? Try installing an external service plugin for more Live TV options."
+    "MessageTunerDeviceNotListed": "Is your tuner device not listed? Try installing an external service provider for more Live TV options."
 }