Browse Source

Merge pull request #1478 from MediaBrowser/dev

Dev
Luke 9 years ago
parent
commit
6d55b56cd6

+ 1 - 54
MediaBrowser.Api/ConnectService.cs

@@ -1,10 +1,8 @@
-using System;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Connect;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Dto;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Linq;
@@ -75,28 +73,6 @@ namespace MediaBrowser.Api
         public string ConnectUserId { get; set; }
     }
 
-    [Route("/Connect/Supporters", "GET")]
-    [Authenticated(Roles = "Admin")]
-    public class GetConnectSupporterSummary : IReturn<ConnectSupporterSummary>
-    {
-    }
-
-    [Route("/Connect/Supporters", "DELETE")]
-    [Authenticated(Roles = "Admin")]
-    public class RemoveConnectSupporter : IReturnVoid
-    {
-        [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
-        public string Id { get; set; }
-    }
-
-    [Route("/Connect/Supporters", "POST")]
-    [Authenticated(Roles = "Admin")]
-    public class AddConnectSupporter : IReturnVoid
-    {
-        [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string Id { get; set; }
-    }
-
     public class ConnectService : BaseApiService
     {
         private readonly IConnectManager _connectManager;
@@ -108,35 +84,6 @@ namespace MediaBrowser.Api
             _userManager = userManager;
         }
 
-        public async Task<object> Get(GetConnectSupporterSummary request)
-        {
-            var result = await _connectManager.GetConnectSupporterSummary().ConfigureAwait(false);
-            var existingConnectUserIds = result.Users.Select(i => i.Id).ToList();
-
-            result.EligibleUsers = _userManager.Users
-                .Where(i => !string.IsNullOrWhiteSpace(i.ConnectUserId))
-                .Where(i => !existingConnectUserIds.Contains(i.ConnectUserId, StringComparer.OrdinalIgnoreCase))
-                .OrderBy(i => i.Name)
-                .Select(i => _userManager.GetUserDto(i))
-                .ToList();
-
-            return ToOptimizedResult(result);
-        }
-
-        public void Delete(RemoveConnectSupporter request)
-        {
-            var task = _connectManager.RemoveConnectSupporter(request.Id);
-
-            Task.WaitAll(task);
-        }
-
-        public void Post(AddConnectSupporter request)
-        {
-            var task = _connectManager.AddConnectSupporter(request.Id);
-
-            Task.WaitAll(task);
-        }
-
         public object Post(CreateConnectLink request)
         {
             return _connectManager.LinkUser(request.Id, request.ConnectUsername);

+ 0 - 20
MediaBrowser.Controller/Connect/IConnectManager.cs

@@ -76,25 +76,5 @@ namespace MediaBrowser.Controller.Connect
         /// <param name="token">The token.</param>
         /// <returns><c>true</c> if [is authorization token valid] [the specified token]; otherwise, <c>false</c>.</returns>
         bool IsAuthorizationTokenValid(string token);
-
-        /// <summary>
-        /// Gets the connect supporter summary.
-        /// </summary>
-        /// <returns>Task&lt;ConnectSupporterSummary&gt;.</returns>
-        Task<ConnectSupporterSummary> GetConnectSupporterSummary();
-
-        /// <summary>
-        /// Removes the connect supporter.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <returns>Task.</returns>
-        Task RemoveConnectSupporter(string id);
-
-        /// <summary>
-        /// Adds the connect supporter.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <returns>Task.</returns>
-        Task AddConnectSupporter(string id);
     }
 }

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

@@ -46,6 +46,9 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
         Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
+    }
+    public interface IConfigurableTunerHost
+    {
         /// <summary>
         /// Validates the specified information.
         /// </summary>

+ 0 - 7
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -182,8 +182,6 @@ namespace MediaBrowser.Model.Configuration
         public PeopleMetadataOptions PeopleMetadataOptions { get; set; }
         public bool FindInternetTrailers { get; set; }
 
-        public string[] InsecureApps9 { get; set; }
-
         public bool SaveMetadataHidden { get; set; }
 
         public NameValuePair[] ContentTypes { get; set; }
@@ -256,11 +254,6 @@ namespace MediaBrowser.Model.Configuration
 
             PeopleMetadataOptions = new PeopleMetadataOptions();
 
-            InsecureApps9 = new[]
-            {
-                "Windows Phone"
-            };
-
             MetadataOptions = new[]
             {
                 new MetadataOptions(1, 1280) {ItemType = "Book"},

+ 0 - 145
MediaBrowser.Server.Implementations/Connect/ConnectManager.cs

@@ -10,7 +10,6 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Model.Connect;
 using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Serialization;
@@ -24,7 +23,6 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
-using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Server.Implementations.Connect
 {
@@ -121,7 +119,6 @@ namespace MediaBrowser.Server.Implementations.Connect
             _securityManager = securityManager;
             _fileSystem = fileSystem;
 
-            _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
             _config.ConfigurationUpdated += _config_ConfigurationUpdated;
 
             LoadCachedData();
@@ -1071,90 +1068,6 @@ namespace MediaBrowser.Server.Implementations.Connect
             }
         }
 
-        public async Task<ConnectSupporterSummary> GetConnectSupporterSummary()
-        {
-            var url = GetConnectUrl("keyAssociation");
-
-            var options = new HttpRequestOptions
-            {
-                Url = url,
-                CancellationToken = CancellationToken.None
-            };
-
-            var postData = new Dictionary<string, string>
-                {
-                    {"serverId", ConnectServerId},
-                    {"supporterKey", _securityManager.SupporterKey}
-                };
-
-            options.SetPostData(postData);
-
-            SetServerAccessToken(options);
-            SetApplicationHeader(options);
-
-            // No need to examine the response
-            using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
-            {
-                return _json.DeserializeFromStream<ConnectSupporterSummary>(stream);
-            }
-        }
-
-        public async Task AddConnectSupporter(string id)
-        {
-            var url = GetConnectUrl("keyAssociation");
-
-            var options = new HttpRequestOptions
-            {
-                Url = url,
-                CancellationToken = CancellationToken.None
-            };
-
-            var postData = new Dictionary<string, string>
-                {
-                    {"serverId", ConnectServerId},
-                    {"supporterKey", _securityManager.SupporterKey},
-                    {"userId", id}
-                };
-
-            options.SetPostData(postData);
-
-            SetServerAccessToken(options);
-            SetApplicationHeader(options);
-
-            // No need to examine the response
-            using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
-            {
-            }
-        }
-
-        public async Task RemoveConnectSupporter(string id)
-        {
-            var url = GetConnectUrl("keyAssociation");
-
-            var options = new HttpRequestOptions
-            {
-                Url = url,
-                CancellationToken = CancellationToken.None
-            };
-
-            var postData = new Dictionary<string, string>
-                {
-                    {"serverId", ConnectServerId},
-                    {"supporterKey", _securityManager.SupporterKey},
-                    {"userId", id}
-                };
-
-            options.SetPostData(postData);
-
-            SetServerAccessToken(options);
-            SetApplicationHeader(options);
-
-            // No need to examine the response
-            using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content)
-            {
-            }
-        }
-
         public async Task Authenticate(string username, string passwordMd5)
         {
             if (string.IsNullOrWhiteSpace(username))
@@ -1186,64 +1099,6 @@ namespace MediaBrowser.Server.Implementations.Connect
             }
         }
 
-        async void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
-        {
-            var user = e.Argument;
-
-            await TryUploadUserPreferences(user, CancellationToken.None).ConfigureAwait(false);
-        }
-
-        private async Task TryUploadUserPreferences(User user, CancellationToken cancellationToken)
-        {
-            if (user == null)
-            {
-                throw new ArgumentNullException("user");
-            }
-
-            if (string.IsNullOrEmpty(user.ConnectUserId))
-            {
-                return;
-            }
-            if (string.IsNullOrEmpty(ConnectAccessKey))
-            {
-                return;
-            }
-
-            var url = GetConnectUrl("user/preferences");
-            url += "?userId=" + user.ConnectUserId;
-            url += "&key=userpreferences";
-
-            var options = new HttpRequestOptions
-            {
-                Url = url,
-                CancellationToken = cancellationToken
-            };
-
-            var postData = new Dictionary<string, string>();
-            postData["data"] = _json.SerializeToString(ConnectUserPreferences.FromUserConfiguration(user.Configuration));
-            options.SetPostData(postData);
-
-            SetServerAccessToken(options);
-            SetApplicationHeader(options);
-
-            try
-            {
-                // No need to examine the response
-                using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
-                {
-                }
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error uploading user preferences", ex);
-            }
-        }
-
-        private async Task DownloadUserPreferences(User user, CancellationToken cancellationToken)
-        {
-
-        }
-
         public async Task<User> GetLocalUser(string connectUserId)
         {
             var user = _userManager.Users

+ 3 - 6
MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs

@@ -134,20 +134,17 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
 
         private bool IsExemptFromAuthenticationToken(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues)
         {
-            if (!_config.Configuration.IsStartupWizardCompleted &&
-                authAttribtues.AllowBeforeStartupWizard)
+            if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard)
             {
                 return true;
             }
 
-            return _config.Configuration.InsecureApps9.Contains(auth.Client ?? string.Empty,
-                StringComparer.OrdinalIgnoreCase);
+            return false;
         }
 
         private bool IsExemptFromRoles(AuthorizationInfo auth, IAuthenticationAttributes authAttribtues, AuthenticationInfo tokenInfo)
         {
-            if (!_config.Configuration.IsStartupWizardCompleted &&
-                authAttribtues.AllowBeforeStartupWizard)
+            if (!_config.Configuration.IsStartupWizardCompleted && authAttribtues.AllowBeforeStartupWizard)
             {
                 return true;
             }

+ 0 - 5
MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -45,7 +45,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
         {
             var auth = GetAuthorizationDictionary(httpReq);
 
-            string userId = null;
             string deviceId = null;
             string device = null;
             string client = null;
@@ -53,9 +52,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
 
             if (auth != null)
             {
-                // TODO: Remove this 
-                auth.TryGetValue("UserId", out userId);
-
                 auth.TryGetValue("DeviceId", out deviceId);
                 auth.TryGetValue("Device", out device);
                 auth.TryGetValue("Client", out client);
@@ -78,7 +74,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
                 Client = client,
                 Device = device,
                 DeviceId = deviceId,
-                UserId = userId,
                 Version = version,
                 Token = token
             };

+ 5 - 1
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -2343,7 +2343,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 throw new ResourceNotFoundException();
             }
 
-            await provider.Validate(info).ConfigureAwait(false);
+            var configurable = provider as IConfigurableTunerHost;
+            if (configurable != null)
+            {
+                await configurable.Validate(info).ConfigureAwait(false);
+            }
 
             var config = GetConfiguration();
 

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -64,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
             return list;
         }
 
-        private List<TunerHostInfo> GetTunerHosts()
+        protected virtual List<TunerHostInfo> GetTunerHosts()
         {
             return GetConfiguration().TunerHosts
                 .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))

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

@@ -20,7 +20,7 @@ using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 {
-    public class HdHomerunHost : BaseTunerHost, ITunerHost
+    public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
     {
         private readonly IHttpClient _httpClient;
 

+ 4 - 77
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -8,19 +8,17 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.MediaInfo;
 using System;
 using System.Collections.Generic;
-using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
-using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
 {
-    public class M3UTunerHost : BaseTunerHost, ITunerHost
+    public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
     {
         private readonly IFileSystem _fileSystem;
         private readonly IHttpClient _httpClient;
@@ -46,65 +44,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
 
         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);
-            }
-        }
-
-        private List<M3UChannel> GetChannels(StreamReader reader, string urlHash)
-        {
-            var channels = new List<M3UChannel>();
-
-            string channnelName = null;
-            string channelNumber = null;
-            string line;
-
-            while ((line = reader.ReadLine()) != null)
-            {
-                line = line.Trim();
-                if (string.IsNullOrWhiteSpace(line))
-                {
-                    continue;
-                }
-
-                if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
-                {
-                    continue;
-                }
-
-                if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
-                {
-                    line = line.Substring(8);
-                    Logger.Info("Found m3u channel: {0}", line);
-                    var parts = line.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 + line.GetMD5().ToString("N"),
-                        Path = line
-                    });
-
-                    channelNumber = null;
-                    channnelName = null;
-                }
-            }
-            return channels;
+            return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(info.Url, ChannelIdPrefix, cancellationToken).ConfigureAwait(false);
         }
 
         public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
         {
-            var list = GetConfiguration().TunerHosts
-            .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
+            var list = GetTunerHosts()
             .Select(i => new LiveTvTunerInfo()
             {
                 Name = Name,
@@ -125,18 +70,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
             return sources.First();
         }
 
-        class M3UChannel : ChannelInfo
-        {
-            public string Path { get; set; }
-
-            public M3UChannel()
-            {
-            }
-        }
-
         public async Task Validate(TunerHostInfo info)
         {
-            using (var stream = await GetListingsStream(info, CancellationToken.None).ConfigureAwait(false))
+            using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
             {
 
             }
@@ -147,15 +83,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
             return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
         }
 
-        private Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
-        {
-            if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
-            {
-                return _httpClient.Get(info.Url, cancellationToken);
-            }
-            return Task.FromResult(_fileSystem.OpenRead(info.Url));
-        }
-
         protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
         {
             var urlHash = info.Url.GetMD5().ToString("N");

+ 102 - 0
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using CommonIO;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Logging;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+{
+    public class M3uParser
+    {
+        private readonly ILogger _logger;
+        private readonly IFileSystem _fileSystem;
+        private readonly IHttpClient _httpClient;
+
+        public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient)
+        {
+            _logger = logger;
+            _fileSystem = fileSystem;
+            _httpClient = httpClient;
+        }
+
+        public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, CancellationToken cancellationToken)
+        {
+            var urlHash = url.GetMD5().ToString("N");
+
+            // Read the file and display it line by line.
+            using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
+            {
+                return GetChannels(reader, urlHash, channelIdPrefix);
+            }
+        }
+
+        public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
+        {
+            if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+            {
+                return _httpClient.Get(url, cancellationToken);
+            }
+            return Task.FromResult(_fileSystem.OpenRead(url));
+        }
+
+        private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix)
+        {
+            var channels = new List<M3UChannel>();
+
+            string channnelName = null;
+            string channelNumber = null;
+            string line;
+
+            while ((line = reader.ReadLine()) != null)
+            {
+                line = line.Trim();
+                if (string.IsNullOrWhiteSpace(line))
+                {
+                    continue;
+                }
+
+                if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
+                {
+                    continue;
+                }
+
+                if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
+                {
+                    line = line.Substring(8);
+                    _logger.Info("Found m3u channel: {0}", line);
+                    var parts = line.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 + line.GetMD5().ToString("N"),
+                        Path = line
+                    });
+
+                    channelNumber = null;
+                    channnelName = null;
+                }
+            }
+            return channels;
+        }
+    }
+
+    public class M3UChannel : ChannelInfo
+    {
+        public string Path { get; set; }
+
+        public M3UChannel()
+        {
+        }
+    }
+}

+ 132 - 41
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs

@@ -1,10 +1,13 @@
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using System.Xml;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dlna;
@@ -13,6 +16,7 @@ using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
 {
@@ -24,14 +28,26 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
         private readonly ILiveTvManager _liveTvManager;
         private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
         private readonly IHttpClient _httpClient;
+        private readonly IJsonSerializer _json;
 
-        public SatIpDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient)
+        public static SatIpDiscovery Current;
+
+        private readonly List<TunerHostInfo> _discoveredHosts = new List<TunerHostInfo>();
+
+        public List<TunerHostInfo> 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()
@@ -66,26 +82,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
 
             try
             {
-                var options = GetConfiguration();
-
-                //if (options.TunerHosts.Any(i =>
-                //            string.Equals(i.Type, SatIpHost.DeviceType, StringComparison.OrdinalIgnoreCase) &&
-                //            UriEquals(i.Url, url)))
-                //{
-                //    return;
-                //}
-
-                //// Strip off the port
-                //url = new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped).TrimEnd('/');
+                if (_discoveredHosts.Any(i => string.Equals(i.Type, SatIpHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(location, i.Url, StringComparison.OrdinalIgnoreCase)))
+                {
+                    return;
+                }
 
-                //await TestUrl(url).ConfigureAwait(false);
+                _logger.Debug("Will attempt to add SAT device {0}", location);
+                var info = await GetInfo(location, CancellationToken.None).ConfigureAwait(false);
 
-                //await _liveTvManager.SaveTunerHost(new TunerHostInfo
-                //{
-                //    Type = SatIpHost.DeviceType,
-                //    Url = url
+                _discoveredHosts.Add(info);
+            }
+            catch (OperationCanceledException)
+            {
 
-                //}).ConfigureAwait(false);
+            }
+            catch (NotImplementedException)
+            {
+                
             }
             catch (Exception ex)
             {
@@ -97,43 +110,121 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
             }
         }
 
-        private async Task TestUrl(string url)
+        public void Dispose()
         {
-            // Test it by pulling down the lineup
-            using (await _httpClient.Get(new HttpRequestOptions
+        }
+
+        public async Task<SatIpTunerHostInfo> GetInfo(string url, CancellationToken cancellationToken)
+        {
+            var result = new SatIpTunerHostInfo
             {
-                Url = string.Format("{0}/lineup.json", url),
-                CancellationToken = CancellationToken.None
-            }))
+                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();
             }
-        }
 
-        private bool UriEquals(string savedUri, string location)
-        {
-            return string.Equals(NormalizeUrl(location), NormalizeUrl(savedUri), StringComparison.OrdinalIgnoreCase);
-        }
+            // Device hasn't implemented an m3u list
+            if (string.IsNullOrWhiteSpace(result.M3UUrl))
+            {
+                result.IsEnabled = false;
+            }
 
-        private string NormalizeUrl(string url)
-        {
-            if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+            else if (!result.M3UUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
             {
-                url = "http://" + url;
+                var fullM3uUrl = url.Substring(0, url.LastIndexOf('/'));
+                result.M3UUrl = fullM3uUrl + "/" + result.M3UUrl.TrimStart('/');
             }
 
-            url = url.TrimEnd('/');
+            _logger.Debug("SAT device result: {0}", _json.SerializeToString(result));
 
-            // Strip off the port
-            return new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped);
+            return result;
         }
 
-        private LiveTvOptions GetConfiguration()
+        private void FillFromDeviceNode(SatIpTunerHostInfo info, XmlReader reader)
         {
-            return _config.GetConfiguration<LiveTvOptions>("livetv");
-        }
+            reader.MoveToContent();
 
-        public void Dispose()
-        {
+            while (reader.Read())
+            {
+                if (reader.NodeType == XmlNodeType.Element)
+                {
+                    switch (reader.Name)
+                    {
+                        case "UDN":
+                            {
+                                info.Id = reader.ReadElementContentAsString();
+                                break;
+                            }
+
+                        case "satip:X_SATIPCAP":
+                        case "X_SATIPCAP":
+                            {
+                                // <satip:X_SATIPCAP xmlns:satip="urn:ses-com:satip">DVBS2-2</satip:X_SATIPCAP>
+                                var value = reader.ReadElementContentAsString();
+                                // TODO
+                                break;
+                            }
+
+                        case "satip:X_SATIPM3U":
+                        case "X_SATIPM3U":
+                            {
+                                // <satip:X_SATIPM3U xmlns:satip="urn:ses-com:satip">/channellist.lua?select=m3u</satip:X_SATIPM3U>
+                                info.M3UUrl = reader.ReadElementContentAsString();
+                                break;
+                            }
+
+                        default:
+                            reader.Skip();
+                            break;
+                    }
+                }
+            }
         }
     }
+
+    public class SatIpTunerHostInfo : TunerHostInfo
+    {
+        public int Tuners { get; set; }
+        public int TunersAvailable { get; set; }
+        public string M3UUrl { get; set; }
+    }
 }

+ 141 - 30
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs

@@ -1,45 +1,156 @@
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using CommonIO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
 {
-    public class SatIpHost /*: BaseTunerHost*/
+    public class SatIpHost : BaseTunerHost, ITunerHost
     {
-        //public SatIpHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder)
-        //    : base(config, logger, jsonSerializer, mediaEncoder)
-        //{
-        //}
+        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<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken)
+        {
+            var satInfo = (SatIpTunerHostInfo) tuner;
 
-        //protected override Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken)
-        //{
-        //    throw new NotImplementedException();
-        //}
+            return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(satInfo.M3UUrl, ChannelIdPrefix, cancellationToken).ConfigureAwait(false);
+        }
 
         public static string DeviceType
         {
             get { return "satip"; }
         }
 
-        //public override string Type
-        //{
-        //    get { return DeviceType; }
-        //}
+        public override string Type
+        {
+            get { return DeviceType; }
+        }
+
+        protected override async Task<List<MediaSourceInfo>> 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<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
+                };
+
+                return new List<MediaSourceInfo> { mediaSource };
+            }
+            return new List<MediaSourceInfo> { };
+        }
+
+        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();
+        }
+
+        protected override async Task<bool> 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 Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
-        //{
-        //    throw new NotImplementedException();
-        //}
+        protected override List<TunerHostInfo> GetTunerHosts()
+        {
+            return SatIpDiscovery.Current.DiscoveredHosts;
+        }
 
-        //protected override Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken)
-        //{
-        //    throw new NotImplementedException();
-        //}
+        public string Name
+        {
+            get { return "Sat IP"; }
+        }
 
-        //protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
-        //{
-        //    throw new NotImplementedException();
-        //}
+        public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
+        {
+            var list = GetTunerHosts()
+            .Select(i => new LiveTvTunerInfo()
+            {
+                Name = Name,
+                SourceType = Type,
+                Status = LiveTvTunerStatus.Available,
+                Id = i.Url.GetMD5().ToString("N"),
+                Url = i.Url
+            })
+            .ToList();
 
-        //protected override bool IsValidChannelId(string channelId)
-        //{
-        //    throw new NotImplementedException();
-        //}
+            return Task.FromResult(list);
+        }
     }
 }

+ 1 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -234,6 +234,7 @@
     <Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" />
+    <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
     <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
     <Compile Include="LiveTv\ProgramImageProvider.cs" />
     <Compile Include="LiveTv\RecordingImageProvider.cs" />