Browse Source

added a shutdown api method, font size fix and other decouplings

LukePulverenti 12 năm trước cách đây
mục cha
commit
6efd22a3d2
30 tập tin đã thay đổi với 420 bổ sung235 xóa
  1. 31 1
      MediaBrowser.Api/SystemService.cs
  2. 7 6
      MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs
  3. 19 24
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  4. 3 3
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  5. 11 3
      MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  6. 10 2
      MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
  7. 0 0
      MediaBrowser.Common.Implementations/Server/RegisterServer.bat
  8. 27 18
      MediaBrowser.Common.Implementations/Server/ServerManager.cs
  9. 5 34
      MediaBrowser.Common.Implementations/Server/WebSocketConnection.cs
  10. 6 48
      MediaBrowser.Common/Kernel/BaseKernel.cs
  11. 6 6
      MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs
  12. 5 0
      MediaBrowser.Common/Kernel/IApplicationHost.cs
  13. 2 9
      MediaBrowser.Common/Kernel/IKernel.cs
  14. 54 0
      MediaBrowser.Common/Kernel/IServerManager.cs
  15. 2 3
      MediaBrowser.Common/MediaBrowser.Common.csproj
  16. 85 0
      MediaBrowser.Common/Net/IWebSocketConnection.cs
  17. 6 0
      MediaBrowser.Common/Net/IWebSocketServer.cs
  18. 2 29
      MediaBrowser.Controller/Kernel.cs
  19. 48 4
      MediaBrowser.Controller/Library/LibraryManager.cs
  20. 2 2
      MediaBrowser.Controller/Library/UserManager.cs
  21. 5 5
      MediaBrowser.Controller/Updates/InstallationManager.cs
  22. 38 2
      MediaBrowser.Networking/Udp/UdpServer.cs
  23. 8 0
      MediaBrowser.Networking/WebSocket/AlchemyServer.cs
  24. 1 2
      MediaBrowser.ServerApplication/App.xaml.cs
  25. 31 24
      MediaBrowser.ServerApplication/ApplicationHost.cs
  26. 0 4
      MediaBrowser.WebDashboard/Html/css/site.css
  27. 1 1
      Nuget/MediaBrowser.ApiClient.nuspec
  28. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  29. 1 1
      Nuget/MediaBrowser.Common.nuspec
  30. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 31 - 1
MediaBrowser.Api/SystemService.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Kernel;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Serialization;
@@ -29,6 +30,11 @@ namespace MediaBrowser.Api
     {
     }
 
+    [Route("/System/Shutdown", "POST")]
+    public class ShutdownApplication
+    {
+    }
+    
     /// <summary>
     /// Class GetConfiguration
     /// </summary>
@@ -61,19 +67,30 @@ namespace MediaBrowser.Api
         /// </summary>
         private readonly IJsonSerializer _jsonSerializer;
 
+        /// <summary>
+        /// The _app host
+        /// </summary>
+        private readonly IApplicationHost _appHost;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="SystemService" /> class.
         /// </summary>
         /// <param name="jsonSerializer">The json serializer.</param>
+        /// <param name="appHost">The app host.</param>
         /// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
-        public SystemService(IJsonSerializer jsonSerializer)
+        public SystemService(IJsonSerializer jsonSerializer, IApplicationHost appHost)
             : base()
         {
             if (jsonSerializer == null)
             {
                 throw new ArgumentNullException("jsonSerializer");
             }
+            if (appHost == null)
+            {
+                throw new ArgumentNullException("appHost");
+            }
 
+            _appHost = appHost;
             _jsonSerializer = jsonSerializer;
         }
 
@@ -118,6 +135,19 @@ namespace MediaBrowser.Api
             });
         }
 
+        /// <summary>
+        /// Posts the specified request.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        public void Post(ShutdownApplication request)
+        {
+            Task.Run(async () =>
+            {
+                await Task.Delay(100);
+                _appHost.Shutdown();
+            });
+        }
+
         /// <summary>
         /// Posts the specified configuraiton.
         /// </summary>

+ 7 - 6
MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Kernel;
-using MediaBrowser.Controller;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
@@ -27,18 +26,20 @@ namespace MediaBrowser.Api.WebSocket
         /// <summary>
         /// The _kernel
         /// </summary>
+        private readonly IApplicationHost _appHost;
         private readonly IKernel _kernel;
-        
+ 
         /// <summary>
         /// Initializes a new instance of the <see cref="LogFileWebSocketListener" /> class.
         /// </summary>
         /// <param name="logger">The logger.</param>
         /// <param name="kernel">The kernel.</param>
-        public LogFileWebSocketListener(ILogger logger, Kernel kernel)
+        public LogFileWebSocketListener(ILogger logger, IApplicationHost host, IKernel kernel)
             : base(logger)
         {
+            _appHost = host;
             _kernel = kernel;
-            _kernel.LoggerLoaded += kernel_LoggerLoaded;
+            kernel.LoggerLoaded += kernel_LoggerLoaded;
         }
 
         /// <summary>
@@ -48,9 +49,9 @@ namespace MediaBrowser.Api.WebSocket
         /// <returns>IEnumerable{System.String}.</returns>
         protected override async Task<IEnumerable<string>> GetDataToSend(LogFileWebSocketState state)
         {
-            if (!string.Equals(_kernel.LogFilePath, state.LastLogFilePath))
+            if (!string.Equals(_appHost.LogFilePath, state.LastLogFilePath))
             {
-                state.LastLogFilePath = _kernel.LogFilePath;
+                state.LastLogFilePath = _appHost.LogFilePath;
                 state.StartLine = 0;
             }
 

+ 19 - 24
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -151,21 +151,22 @@ namespace MediaBrowser.Common.Implementations
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="obj">The obj.</param>
-        protected void RegisterSingleInstance<T>(T obj)
+        /// <param name="manageLifetime">if set to <c>true</c> [manage lifetime].</param>
+        protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
             where T : class
         {
             Container.RegisterSingle(obj);
-        }
 
-        /// <summary>
-        /// Registers the specified func.
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="func">The func.</param>
-        protected void Register<T>(Func<T> func)
-            where T : class
-        {
-            Container.Register(func);
+            if (manageLifetime)
+            {
+                var disposable = obj as IDisposable;
+
+                if (disposable != null)
+                {
+                    Logger.Info("Registering " + disposable.GetType().Name);
+                    DisposableParts.Add(disposable);
+                }
+            }
         }
 
         /// <summary>
@@ -205,16 +206,6 @@ namespace MediaBrowser.Common.Implementations
             return (T)result.GetInstance();
         }
 
-        /// <summary>
-        /// Registers the specified service type.
-        /// </summary>
-        /// <param name="serviceType">Type of the service.</param>
-        /// <param name="implementation">Type of the concrete.</param>
-        protected void Register(Type serviceType, Type implementation)
-        {
-            Container.Register(serviceType, implementation);
-        }
-
         /// <summary>
         /// Loads the assembly.
         /// </summary>
@@ -282,13 +273,17 @@ namespace MediaBrowser.Common.Implementations
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
         protected virtual void Dispose(bool dispose)
         {
-            foreach (var part in DisposableParts)
+            var type = GetType();
+
+            Logger.Info("Disposing " + type.Name);
+
+            foreach (var part in DisposableParts.Distinct().Where(i => i.GetType() != type).ToList())
             {
+                Logger.Info("Disposing " + part.GetType().Name);
+
                 part.Dispose();
             }
 
-            var b = Container.GetCurrentRegistrations();
-
             DisposableParts.Clear();
         }
     }

+ 3 - 3
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -47,10 +47,7 @@
     <Reference Include="System" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
-    <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
@@ -69,6 +66,8 @@
     <Compile Include="Serialization\JsonSerializer.cs" />
     <Compile Include="Serialization\ProtobufSerializer.cs" />
     <Compile Include="Serialization\XmlSerializer.cs" />
+    <Compile Include="Server\ServerManager.cs" />
+    <Compile Include="Server\WebSocketConnection.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@@ -82,6 +81,7 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
+    <EmbeddedResource Include="Server\RegisterServer.bat" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(SolutionDir)\.nuget\nuget.targets" />

+ 11 - 3
MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -48,6 +48,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <value>The task manager.</value>
         private ITaskManager TaskManager { get; set; }
 
+        /// <summary>
+        /// Gets or sets the server manager.
+        /// </summary>
+        /// <value>The server manager.</value>
+        private IServerManager ServerManager { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
         /// </summary>
@@ -56,13 +62,15 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <param name="taskManager">The task manager.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
-        public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
+        /// <param name="serverManager">The server manager.</param>
+        public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IServerManager serverManager)
         {
             ScheduledTask = scheduledTask;
             ApplicationPaths = applicationPaths;
             TaskManager = taskManager;
             JsonSerializer = jsonSerializer;
             Logger = logger;
+            ServerManager = serverManager;
         }
 
         /// <summary>
@@ -302,7 +310,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
             TaskCompletionStatus status;
             CurrentExecutionStartTime = DateTime.UtcNow;
 
-            //Kernel.TcpManager.SendWebSocketMessage("ScheduledTaskBeginExecute", Name);
+            ServerManager.SendWebSocketMessage("ScheduledTaskBeginExecute", Name);
 
             try
             {
@@ -324,7 +332,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
             var startTime = CurrentExecutionStartTime;
             var endTime = DateTime.UtcNow;
 
-            //Kernel.TcpManager.SendWebSocketMessage("ScheduledTaskEndExecute", LastExecutionResult);
+            ServerManager.SendWebSocketMessage("ScheduledTaskEndExecute", LastExecutionResult);
 
             progress.ProgressChanged -= progress_ProgressChanged;
             CurrentCancellationTokenSource.Dispose();

+ 10 - 2
MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs

@@ -43,18 +43,26 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <value>The logger.</value>
         private ILogger Logger { get; set; }
 
+        /// <summary>
+        /// Gets or sets the server manager.
+        /// </summary>
+        /// <value>The server manager.</value>
+        private IServerManager ServerManager { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="TaskManager" /> class.
         /// </summary>
         /// <param name="applicationPaths">The application paths.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
+        /// <param name="serverManager">The server manager.</param>
         /// <exception cref="System.ArgumentException">kernel</exception>
-        public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger)
+        public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IServerManager serverManager)
         {
             ApplicationPaths = applicationPaths;
             JsonSerializer = jsonSerializer;
             Logger = logger;
+            ServerManager = serverManager;
 
             ScheduledTasks = new IScheduledTaskWorker[] { };
         }
@@ -155,7 +163,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         {
             var myTasks = ScheduledTasks.ToList();
 
-            myTasks.AddRange(tasks.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger)));
+            myTasks.AddRange(tasks.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, ServerManager)));
 
             ScheduledTasks = myTasks.ToArray();
         }

+ 0 - 0
MediaBrowser.Common/Kernel/RegisterServer.bat → MediaBrowser.Common.Implementations/Server/RegisterServer.bat


+ 27 - 18
MediaBrowser.Common/Kernel/TcpManager.cs → MediaBrowser.Common.Implementations/Server/ServerManager.cs

@@ -1,5 +1,5 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Common.Kernel;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using System;
@@ -14,12 +14,12 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace MediaBrowser.Common.Kernel
+namespace MediaBrowser.Common.Implementations.Server
 {
     /// <summary>
     /// Manages the Http Server, Udp Server and WebSocket connections
     /// </summary>
-    public class TcpManager : IDisposable
+    public class ServerManager : IServerManager, IDisposable
     {
         /// <summary>
         /// This is the udp server used for server discovery by clients
@@ -49,7 +49,7 @@ namespace MediaBrowser.Common.Kernel
         /// <summary>
         /// The web socket connections
         /// </summary>
-        private readonly List<WebSocketConnection> _webSocketConnections = new List<WebSocketConnection>();
+        private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
 
         /// <summary>
         /// Gets or sets the external web socket server.
@@ -81,7 +81,7 @@ namespace MediaBrowser.Common.Kernel
         /// Gets a value indicating whether [supports web socket].
         /// </summary>
         /// <value><c>true</c> if [supports web socket]; otherwise, <c>false</c>.</value>
-        internal bool SupportsNativeWebSocket
+        public bool SupportsNativeWebSocket
         {
             get { return HttpServer != null && HttpServer.SupportsWebSockets; }
         }
@@ -96,7 +96,7 @@ namespace MediaBrowser.Common.Kernel
         }
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="TcpManager" /> class.
+        /// Initializes a new instance of the <see cref="ServerManager" /> class.
         /// </summary>
         /// <param name="applicationHost">The application host.</param>
         /// <param name="kernel">The kernel.</param>
@@ -104,7 +104,7 @@ namespace MediaBrowser.Common.Kernel
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="logger">The logger.</param>
         /// <exception cref="System.ArgumentNullException">applicationHost</exception>
-        public TcpManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger)
+        public ServerManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger)
         {
             if (applicationHost == null)
             {
@@ -132,8 +132,14 @@ namespace MediaBrowser.Common.Kernel
             _kernel = kernel;
             _applicationHost = applicationHost;
             _networkManager = networkManager;
+        }
 
-            if (applicationHost.IsFirstRun)
+        /// <summary>
+        /// Starts this instance.
+        /// </summary>
+        public void Start()
+        {
+            if (_applicationHost.IsFirstRun)
             {
                 RegisterServerWithAdministratorAccess();
             }
@@ -145,6 +151,8 @@ namespace MediaBrowser.Common.Kernel
             {
                 ReloadExternalWebSocketServer();
             }
+
+            _kernel.ConfigurationUpdated += _kernel_ConfigurationUpdated;
         }
 
         /// <summary>
@@ -170,7 +178,7 @@ namespace MediaBrowser.Common.Kernel
         /// Restarts the Http Server, or starts it if not currently running
         /// </summary>
         /// <param name="registerServerOnFailure">if set to <c>true</c> [register server on failure].</param>
-        public void ReloadHttpServer(bool registerServerOnFailure = true)
+        private void ReloadHttpServer(bool registerServerOnFailure = true)
         {
             // Only reload if the port has changed, so that we don't disconnect any active users
             if (HttpServer != null && HttpServer.UrlPrefix.Equals(_kernel.HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
@@ -426,7 +434,7 @@ namespace MediaBrowser.Common.Kernel
             var tmpFile = Path.Combine(_kernel.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".bat");
 
             // Extract the bat file
-            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Common.Kernel.RegisterServer.bat"))
+            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Common.Implementations.Server.bat"))
             {
                 using (var fileStream = File.Create(tmpFile))
                 {
@@ -489,20 +497,21 @@ namespace MediaBrowser.Common.Kernel
         }
 
         /// <summary>
-        /// Called when [application configuration changed].
+        /// Handles the ConfigurationUpdated event of the _kernel control.
         /// </summary>
-        /// <param name="oldConfig">The old config.</param>
-        /// <param name="newConfig">The new config.</param>
-        public void OnApplicationConfigurationChanged(BaseApplicationConfiguration oldConfig, BaseApplicationConfiguration newConfig)
+        /// <param name="sender">The source of the event.</param>
+        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
+        /// <exception cref="System.NotImplementedException"></exception>
+        void _kernel_ConfigurationUpdated(object sender, EventArgs e)
         {
-            HttpServer.EnableHttpRequestLogging = newConfig.EnableHttpLevelLogging;
+            HttpServer.EnableHttpRequestLogging = _kernel.Configuration.EnableHttpLevelLogging;
 
-            if (oldConfig.HttpServerPortNumber != newConfig.HttpServerPortNumber)
+            if (!string.Equals(HttpServer.UrlPrefix, _kernel.HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
             {
                 ReloadHttpServer();
             }
 
-            if (!SupportsNativeWebSocket && oldConfig.LegacyWebSocketPortNumber != newConfig.LegacyWebSocketPortNumber)
+            if (!SupportsNativeWebSocket && ExternalWebSocketServer != null && ExternalWebSocketServer.Port != _kernel.Configuration.LegacyWebSocketPortNumber)
             {
                 ReloadExternalWebSocketServer();
             }

+ 5 - 34
MediaBrowser.Common/Net/WebSocketConnection.cs → MediaBrowser.Common.Implementations/Server/WebSocketConnection.cs

@@ -1,16 +1,17 @@
-using MediaBrowser.Model.Logging;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using System;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace MediaBrowser.Common.Net
+namespace MediaBrowser.Common.Implementations.Server
 {
     /// <summary>
     /// Class WebSocketConnection
     /// </summary>
-    public class WebSocketConnection : IDisposable
+    public class WebSocketConnection : IWebSocketConnection
     {
         /// <summary>
         /// The _socket
@@ -20,7 +21,7 @@ namespace MediaBrowser.Common.Net
         /// <summary>
         /// The _remote end point
         /// </summary>
-        public readonly string RemoteEndPoint;
+        public string RemoteEndPoint { get; private set; }
 
         /// <summary>
         /// The _cancellation token source
@@ -221,34 +222,4 @@ namespace MediaBrowser.Common.Net
             }
         }
     }
-
-    /// <summary>
-    /// Class WebSocketMessage
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    public class WebSocketMessage<T>
-    {
-        /// <summary>
-        /// Gets or sets the type of the message.
-        /// </summary>
-        /// <value>The type of the message.</value>
-        public string MessageType { get; set; }
-        /// <summary>
-        /// Gets or sets the data.
-        /// </summary>
-        /// <value>The data.</value>
-        public T Data { get; set; }
-    }
-
-    /// <summary>
-    /// Class WebSocketMessageInfo
-    /// </summary>
-    public class WebSocketMessageInfo : WebSocketMessage<string>
-    {
-        /// <summary>
-        /// Gets or sets the connection.
-        /// </summary>
-        /// <value>The connection.</value>
-        public WebSocketConnection Connection { get; set; }
-    }
 }

+ 6 - 48
MediaBrowser.Common/Kernel/BaseKernel.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Logging;
@@ -165,7 +164,7 @@ namespace MediaBrowser.Common.Kernel
         /// Gets or sets the TCP manager.
         /// </summary>
         /// <value>The TCP manager.</value>
-        public TcpManager TcpManager { get; private set; }
+        public IServerManager ServerManager { get; private set; }
 
         /// <summary>
         /// Gets the UDP server port number.
@@ -202,15 +201,6 @@ namespace MediaBrowser.Common.Kernel
         /// <value>The kernel context.</value>
         public abstract KernelContext KernelContext { get; }
 
-        /// <summary>
-        /// Gets the log file path.
-        /// </summary>
-        /// <value>The log file path.</value>
-        public string LogFilePath
-        {
-            get { return ApplicationHost.LogFilePath; }
-        }
-
         /// <summary>
         /// Gets the logger.
         /// </summary>
@@ -238,23 +228,6 @@ namespace MediaBrowser.Common.Kernel
         /// <exception cref="System.ArgumentNullException">isoManager</exception>
         protected BaseKernel(IApplicationHost appHost, TApplicationPathsType appPaths, IXmlSerializer xmlSerializer, ILogger logger)
         {
-            if (appHost == null)
-            {
-                throw new ArgumentNullException("appHost");
-            }
-            if (appPaths == null)
-            {
-                throw new ArgumentNullException("appPaths");
-            }
-            if (xmlSerializer == null)
-            {
-                throw new ArgumentNullException("xmlSerializer");
-            }
-            if (logger == null)
-            {
-                throw new ArgumentNullException("logger");
-            }
-
             ApplicationPaths = appPaths;
             ApplicationHost = appHost;
             _xmlSerializer = xmlSerializer;
@@ -291,8 +264,8 @@ namespace MediaBrowser.Common.Kernel
 
             await OnComposablePartsLoaded().ConfigureAwait(false);
 
-            DisposeTcpManager();
-            TcpManager = (TcpManager)ApplicationHost.CreateInstance(typeof(TcpManager));
+            ServerManager = ApplicationHost.Resolve<IServerManager>();
+            ServerManager.Start();
         }
 
         /// <summary>
@@ -357,7 +330,7 @@ namespace MediaBrowser.Common.Kernel
         {
             HasPendingRestart = true;
 
-            TcpManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
+            ServerManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
 
             EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
         }
@@ -377,22 +350,7 @@ namespace MediaBrowser.Common.Kernel
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
         protected virtual void Dispose(bool dispose)
         {
-            if (dispose)
-            {
-                DisposeTcpManager();
-            }
-        }
 
-        /// <summary>
-        /// Disposes the TCP manager.
-        /// </summary>
-        private void DisposeTcpManager()
-        {
-            if (TcpManager != null)
-            {
-                TcpManager.Dispose();
-                TcpManager = null;
-            }
         }
 
         /// <summary>
@@ -424,8 +382,8 @@ namespace MediaBrowser.Common.Kernel
                 HasPendingRestart = HasPendingRestart,
                 Version = ApplicationHost.ApplicationVersion.ToString(),
                 IsNetworkDeployed = ApplicationHost.CanSelfUpdate,
-                WebSocketPortNumber = TcpManager.WebSocketPortNumber,
-                SupportsNativeWebSocket = TcpManager.SupportsNativeWebSocket,
+                WebSocketPortNumber = ServerManager.WebSocketPortNumber,
+                SupportsNativeWebSocket = ServerManager.SupportsNativeWebSocket,
                 FailedPluginAssemblies = ApplicationHost.FailedAssemblies.ToArray()
             };
         }

+ 6 - 6
MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs

@@ -19,8 +19,8 @@ namespace MediaBrowser.Common.Kernel
         /// <summary>
         /// The _active connections
         /// </summary>
-        protected readonly List<Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
-            new List<Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
+        protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
+            new List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
 
         /// <summary>
         /// Gets the name.
@@ -103,7 +103,7 @@ namespace MediaBrowser.Common.Kernel
 
             lock (ActiveConnections)
             {
-                ActiveConnections.Add(new Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
+                ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
             }
 
             timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs));
@@ -115,9 +115,9 @@ namespace MediaBrowser.Common.Kernel
         /// <param name="state">The state.</param>
         private async void TimerCallback(object state)
         {
-            var connection = (WebSocketConnection)state;
+            var connection = (IWebSocketConnection)state;
 
-            Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
+            Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
 
             lock (ActiveConnections)
             {
@@ -187,7 +187,7 @@ namespace MediaBrowser.Common.Kernel
         /// Disposes the connection.
         /// </summary>
         /// <param name="connection">The connection.</param>
-        private void DisposeConnection(Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
+        private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
         {
             Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
 

+ 5 - 0
MediaBrowser.Common/Kernel/IApplicationHost.cs

@@ -97,5 +97,10 @@ namespace MediaBrowser.Common.Kernel
         /// <typeparam name="T"></typeparam>
         /// <returns>``0.</returns>
         T TryResolve<T>();
+
+        /// <summary>
+        /// Shuts down.
+        /// </summary>
+        void Shutdown();
     }
 }

+ 2 - 9
MediaBrowser.Common/Kernel/IKernel.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Plugins;
+using MediaBrowser.Common.Plugins;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.System;
 using System;
@@ -71,12 +70,6 @@ namespace MediaBrowser.Common.Kernel
         /// <value>The name of the web application.</value>
         string WebApplicationName { get; }
 
-        /// <summary>
-        /// Gets the log file path.
-        /// </summary>
-        /// <value>The log file path.</value>
-        string LogFilePath { get; }
-
         /// <summary>
         /// Performs the pending restart.
         /// </summary>
@@ -104,7 +97,7 @@ namespace MediaBrowser.Common.Kernel
         /// Gets the TCP manager.
         /// </summary>
         /// <value>The TCP manager.</value>
-        TcpManager TcpManager { get; }
+        IServerManager ServerManager { get; }
 
         /// <summary>
         /// Gets the web socket listeners.

+ 54 - 0
MediaBrowser.Common/Kernel/IServerManager.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Kernel
+{
+    public interface IServerManager : IDisposable
+    {
+        /// <summary>
+        /// Gets a value indicating whether [supports web socket].
+        /// </summary>
+        /// <value><c>true</c> if [supports web socket]; otherwise, <c>false</c>.</value>
+        bool SupportsNativeWebSocket { get; }
+
+        /// <summary>
+        /// Gets the web socket port number.
+        /// </summary>
+        /// <value>The web socket port number.</value>
+        int WebSocketPortNumber { get; }
+
+        /// <summary>
+        /// Starts this instance.
+        /// </summary>
+        void Start();
+
+        /// <summary>
+        /// Sends a message to all clients currently connected via a web socket
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="data">The data.</param>
+        /// <returns>Task.</returns>
+        void SendWebSocketMessage<T>(string messageType, T data);
+
+        /// <summary>
+        /// Sends a message to all clients currently connected via a web socket
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
+        void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction);
+
+        /// <summary>
+        /// Sends a message to all clients currently connected via a web socket
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException">messageType</exception>
+        Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken);
+    }
+}

+ 2 - 3
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -91,10 +91,10 @@
     <Compile Include="IO\StreamDefaults.cs" />
     <Compile Include="Kernel\BasePeriodicWebSocketListener.cs" />
     <Compile Include="Kernel\IApplicationPaths.cs" />
+    <Compile Include="Kernel\IServerManager.cs" />
     <Compile Include="Kernel\IWebSocketListener.cs" />
     <Compile Include="Kernel\IApplicationHost.cs" />
     <Compile Include="Kernel\IKernel.cs" />
-    <Compile Include="Kernel\TcpManager.cs" />
     <Compile Include="Net\Handlers\IHttpServerHandler.cs" />
     <Compile Include="Net\Handlers\StaticFileHandler.cs" />
     <Compile Include="Net\IHttpClient.cs" />
@@ -103,12 +103,12 @@
     <Compile Include="Net\IRestfulService.cs" />
     <Compile Include="Net\IUdpServer.cs" />
     <Compile Include="Net\IWebSocket.cs" />
+    <Compile Include="Net\IWebSocketConnection.cs" />
     <Compile Include="Net\IWebSocketServer.cs" />
     <Compile Include="Net\MimeTypes.cs" />
     <Compile Include="Net\StreamWriter.cs" />
     <Compile Include="Net\UdpMessageReceivedEventArgs.cs" />
     <Compile Include="Net\WebSocketConnectEventArgs.cs" />
-    <Compile Include="Net\WebSocketConnection.cs" />
     <Compile Include="Net\WebSocketMessageType.cs" />
     <Compile Include="Net\WebSocketState.cs" />
     <Compile Include="Plugins\BaseUiPlugin.cs" />
@@ -138,7 +138,6 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
-    <EmbeddedResource Include="Kernel\RegisterServer.bat" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>

+ 85 - 0
MediaBrowser.Common/Net/IWebSocketConnection.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Net
+{
+    public interface IWebSocketConnection : IDisposable
+    {
+        /// <summary>
+        /// Gets or sets the receive action.
+        /// </summary>
+        /// <value>The receive action.</value>
+        Action<WebSocketMessageInfo> OnReceive { get; set; }
+
+        /// <summary>
+        /// Gets the state.
+        /// </summary>
+        /// <value>The state.</value>
+        WebSocketState State { get; }
+
+        /// <summary>
+        /// Gets the remote end point.
+        /// </summary>
+        /// <value>The remote end point.</value>
+        string RemoteEndPoint { get; }
+
+        /// <summary>
+        /// Sends a message asynchronously.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="message">The message.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException">message</exception>
+        Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends a message asynchronously.
+        /// </summary>
+        /// <param name="buffer">The buffer.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task SendAsync(byte[] buffer, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends a message asynchronously.
+        /// </summary>
+        /// <param name="buffer">The buffer.</param>
+        /// <param name="type">The type.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException">buffer</exception>
+        Task SendAsync(byte[] buffer, WebSocketMessageType type, CancellationToken cancellationToken);
+    }
+
+    /// <summary>
+    /// Class WebSocketMessage
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class WebSocketMessage<T>
+    {
+        /// <summary>
+        /// Gets or sets the type of the message.
+        /// </summary>
+        /// <value>The type of the message.</value>
+        public string MessageType { get; set; }
+        /// <summary>
+        /// Gets or sets the data.
+        /// </summary>
+        /// <value>The data.</value>
+        public T Data { get; set; }
+    }
+
+    /// <summary>
+    /// Class WebSocketMessageInfo
+    /// </summary>
+    public class WebSocketMessageInfo : WebSocketMessage<string>
+    {
+        /// <summary>
+        /// Gets or sets the connection.
+        /// </summary>
+        /// <value>The connection.</value>
+        public IWebSocketConnection Connection { get; set; }
+    }
+}

+ 6 - 0
MediaBrowser.Common/Net/IWebSocketServer.cs

@@ -22,5 +22,11 @@ namespace MediaBrowser.Common.Net
         /// Occurs when [web socket connected].
         /// </summary>
         event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
+
+        /// <summary>
+        /// Gets the port.
+        /// </summary>
+        /// <value>The port.</value>
+        int Port { get; }
     }
 }

+ 2 - 29
MediaBrowser.Controller/Kernel.cs

@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Playback;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Controller.ScheduledTasks;
 using MediaBrowser.Controller.Updates;
 using MediaBrowser.Controller.Weather;
 using MediaBrowser.Model.Configuration;
@@ -293,8 +292,6 @@ namespace MediaBrowser.Controller
             get { return 7359; }
         }
 
-        private readonly ITaskManager _taskManager;
-
         /// <summary>
         /// Creates a kernel based on a Data path, which is akin to our current programdata path
         /// </summary>
@@ -304,13 +301,11 @@ namespace MediaBrowser.Controller
         /// <param name="taskManager">The task manager.</param>
         /// <param name="logger">The logger.</param>
         /// <exception cref="System.ArgumentNullException">isoManager</exception>
-        public Kernel(IApplicationHost appHost, IServerApplicationPaths appPaths, IXmlSerializer xmlSerializer, ITaskManager taskManager, ILogger logger)
+        public Kernel(IApplicationHost appHost, IServerApplicationPaths appPaths, IXmlSerializer xmlSerializer, ILogger logger)
             : base(appHost, appPaths, xmlSerializer, logger)
         {
             Instance = this;
 
-            _taskManager = taskManager;
-
             // For now there's no real way to inject this properly
             BaseItem.Logger = logger;
             Ratings.Logger = logger;
@@ -469,7 +464,7 @@ namespace MediaBrowser.Controller
         {
             DisposeFileSystemManager();
 
-            FileSystemManager = new FileSystemManager(this, Logger, _taskManager);
+            FileSystemManager = new FileSystemManager(this, Logger, ApplicationHost.Resolve<ITaskManager>());
             FileSystemManager.StartWatchers();
         }
 
@@ -540,18 +535,6 @@ namespace MediaBrowser.Controller
 
             var reloadLogger = config.ShowLogWindow != oldConfiguration.ShowLogWindow;
 
-            // Figure out whether or not we should refresh people after the update is finished
-            var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders;
-
-            // This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes
-            if (!refreshPeopleAfterUpdate)
-            {
-                var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
-                var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
-
-                refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages;
-            }
-
             Configuration = config;
             SaveConfiguration();
 
@@ -560,20 +543,10 @@ namespace MediaBrowser.Controller
                 ReloadLogger();
             }
 
-            TcpManager.OnApplicationConfigurationChanged(oldConfiguration, config);
-
             // Validate currently executing providers, in the background
             Task.Run(() =>
             {
                 ProviderManager.ValidateCurrentlyRunningProviders();
-
-                // Any number of configuration settings could change the way the library is refreshed, so do that now
-                _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
-
-                if (refreshPeopleAfterUpdate)
-                {
-                    _taskManager.CancelIfRunningAndQueue<PeopleValidationTask>();
-                }
             });
         }
 

+ 48 - 4
MediaBrowser.Controller/Library/LibraryManager.cs

@@ -1,8 +1,10 @@
 using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Controller.ScheduledTasks;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MoreLinq;
@@ -20,7 +22,7 @@ namespace MediaBrowser.Controller.Library
     /// <summary>
     /// Class LibraryManager
     /// </summary>
-    public class LibraryManager : BaseManager<Kernel>
+    public class LibraryManager
     {
         #region LibraryChanged Event
         /// <summary>
@@ -48,7 +50,7 @@ namespace MediaBrowser.Controller.Library
         private void SendLibraryChangedWebSocketMessage(ChildrenChangedEventArgs args)
         {
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(args));
+            Kernel.ServerManager.SendWebSocketMessage("LibraryChanged", () => DtoBuilder.GetLibraryUpdateInfo(args));
         }
         #endregion
 
@@ -57,15 +59,57 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         private readonly ILogger _logger;
 
+        /// <summary>
+        /// The _task manager
+        /// </summary>
+        private readonly ITaskManager _taskManager;
+
+        /// <summary>
+        /// Gets or sets the kernel.
+        /// </summary>
+        /// <value>The kernel.</value>
+        private Kernel Kernel { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryManager" /> class.
         /// </summary>
         /// <param name="kernel">The kernel.</param>
         /// <param name="logger">The logger.</param>
-        public LibraryManager(Kernel kernel, ILogger logger)
-            : base(kernel)
+        /// <param name="taskManager">The task manager.</param>
+        public LibraryManager(Kernel kernel, ILogger logger, ITaskManager taskManager)
         {
+            Kernel = kernel;
             _logger = logger;
+            _taskManager = taskManager;
+
+            kernel.ConfigurationUpdated += kernel_ConfigurationUpdated;
+        }
+
+        /// <summary>
+        /// Handles the ConfigurationUpdated event of the kernel control.
+        /// </summary>
+        /// <param name="sender">The source of the event.</param>
+        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
+        void kernel_ConfigurationUpdated(object sender, EventArgs e)
+        {
+            //// Figure out whether or not we should refresh people after the update is finished
+            //var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders;
+
+            //// This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes
+            //if (!refreshPeopleAfterUpdate)
+            //{
+            //    var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
+            //    var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
+
+            //    refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages;
+            //}
+
+            Task.Run(() =>
+            {
+                // Any number of configuration settings could change the way the library is refreshed, so do that now
+                _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
+                _taskManager.CancelIfRunningAndQueue<PeopleValidationTask>();
+            });
         }
 
         /// <summary>

+ 2 - 2
MediaBrowser.Controller/Library/UserManager.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Controller.Library
             EventHelper.QueueEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
 
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("UserUpdated", new DtoBuilder(_logger).GetDtoUser(user));
+            Kernel.ServerManager.SendWebSocketMessage("UserUpdated", new DtoBuilder(_logger).GetDtoUser(user));
         }
         #endregion
 
@@ -91,7 +91,7 @@ namespace MediaBrowser.Controller.Library
             EventHelper.QueueEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);
 
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("UserDeleted", user.Id.ToString());
+            Kernel.ServerManager.SendWebSocketMessage("UserDeleted", user.Id.ToString());
         }
         #endregion
 

+ 5 - 5
MediaBrowser.Controller/Updates/InstallationManager.cs

@@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Updates
             EventHelper.QueueEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger);
 
             // Notify connected ui's
-            Kernel.TcpManager.SendWebSocketMessage("PluginUninstalled", plugin.GetPluginInfo());
+            Kernel.ServerManager.SendWebSocketMessage("PluginUninstalled", plugin.GetPluginInfo());
         }
         #endregion
 
@@ -371,7 +371,7 @@ namespace MediaBrowser.Controller.Updates
 
             var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
 
-            Kernel.TcpManager.SendWebSocketMessage("PackageInstalling", installationInfo);
+            Kernel.ServerManager.SendWebSocketMessage("PackageInstalling", installationInfo);
 
             try
             {
@@ -384,7 +384,7 @@ namespace MediaBrowser.Controller.Updates
 
                 CompletedInstallations.Add(installationInfo);
 
-                Kernel.TcpManager.SendWebSocketMessage("PackageInstallationCompleted", installationInfo);
+                Kernel.ServerManager.SendWebSocketMessage("PackageInstallationCompleted", installationInfo);
             }
             catch (OperationCanceledException)
             {
@@ -395,7 +395,7 @@ namespace MediaBrowser.Controller.Updates
 
                 _logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr);
 
-                Kernel.TcpManager.SendWebSocketMessage("PackageInstallationCancelled", installationInfo);
+                Kernel.ServerManager.SendWebSocketMessage("PackageInstallationCancelled", installationInfo);
 
                 throw;
             }
@@ -406,7 +406,7 @@ namespace MediaBrowser.Controller.Updates
                     CurrentInstallations.Remove(tuple);
                 }
 
-                Kernel.TcpManager.SendWebSocketMessage("PackageInstallationFailed", installationInfo);
+                Kernel.ServerManager.SendWebSocketMessage("PackageInstallationFailed", installationInfo);
 
                 throw;
             }

+ 38 - 2
MediaBrowser.Networking/Udp/UdpServer.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
 using MediaBrowser.Networking.Management;
 using System;
 using System.Net;
@@ -19,6 +20,21 @@ namespace MediaBrowser.Networking.Udp
         /// </summary>
         public event EventHandler<UdpMessageReceivedEventArgs> MessageReceived;
 
+        /// <summary>
+        /// Gets or sets the logger.
+        /// </summary>
+        /// <value>The logger.</value>
+        private ILogger Logger { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UdpServer" /> class.
+        /// </summary>
+        /// <param name="logger">The logger.</param>
+        public UdpServer(ILogger logger)
+        {
+            Logger = logger;
+        }
+
         /// <summary>
         /// Raises the <see cref="E:MessageReceived" /> event.
         /// </summary>
@@ -54,8 +70,24 @@ namespace MediaBrowser.Networking.Udp
         private IObservable<UdpReceiveResult> CreateObservable()
         {
             return Observable.Create<UdpReceiveResult>(obs =>
-                                Observable.FromAsync(() => _udpClient.ReceiveAsync())
-                                          .Subscribe(obs))
+                                Observable.FromAsync(() =>
+                                {
+                                    try
+                                    {
+                                        return _udpClient.ReceiveAsync();
+                                    }
+                                    catch (ObjectDisposedException)
+                                    {
+                                        return Task.FromResult(new UdpReceiveResult(new byte[]{}, new IPEndPoint(IPAddress.Any, 0)));
+                                    }
+                                    catch (Exception ex)
+                                    {
+                                        Logger.ErrorException("Error receiving udp message", ex);
+                                        return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0)));
+                                    }
+                                })
+
+                             .Subscribe(obs))
                              .Repeat()
                              .Retry()
                              .Publish()
@@ -68,6 +100,10 @@ namespace MediaBrowser.Networking.Udp
         /// <param name="message">The message.</param>
         private void OnMessageReceived(UdpReceiveResult message)
         {
+            if (message.RemoteEndPoint.Port == 0)
+            {
+                return;
+            }
             var bytes = message.Buffer;
 
             OnMessageReceived(new UdpMessageReceivedEventArgs

+ 8 - 0
MediaBrowser.Networking/WebSocket/AlchemyServer.cs

@@ -42,6 +42,12 @@ namespace MediaBrowser.Networking.WebSocket
             _logger = logger;
         }
 
+        /// <summary>
+        /// Gets the port.
+        /// </summary>
+        /// <value>The port.</value>
+        public int Port { get; private set; }
+
         /// <summary>
         /// Starts the specified port number.
         /// </summary>
@@ -56,6 +62,8 @@ namespace MediaBrowser.Networking.WebSocket
 
             WebSocketServer.Start();
 
+            Port = portNumber;
+
             _logger.Info("Alchemy Web Socket Server started");
         }
 

+ 1 - 2
MediaBrowser.ServerApplication/App.xaml.cs

@@ -255,7 +255,6 @@ namespace MediaBrowser.ServerApplication
 
             base.OnExit(e);
 
-            Kernel.Dispose();
             CompositionRoot.Dispose();
         }
 
@@ -360,7 +359,7 @@ namespace MediaBrowser.ServerApplication
         {
             Dispatcher.Invoke(ReleaseMutex);
 
-            Kernel.Dispose();
+            CompositionRoot.Dispose();
 
             System.Windows.Forms.Application.Restart();
 

+ 31 - 24
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Common.Implementations;
 using MediaBrowser.Common.Implementations.ScheduledTasks;
 using MediaBrowser.Common.Implementations.Serialization;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Implementations.Server;
 using MediaBrowser.Common.Kernel;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.ScheduledTasks;
@@ -65,16 +66,6 @@ namespace MediaBrowser.ServerApplication
         /// </summary>
         private readonly IServerApplicationPaths _applicationPaths = new ServerApplicationPaths();
 
-        /// <summary>
-        /// The _task manager
-        /// </summary>
-        private readonly ITaskManager _taskManager;
-
-        /// <summary>
-        /// The _task manager
-        /// </summary>
-        private readonly IHttpServer _httpServer;
-
         /// <summary>
         /// Gets a value indicating whether this instance is first run.
         /// </summary>
@@ -89,29 +80,34 @@ namespace MediaBrowser.ServerApplication
             : base()
         {
             IsFirstRun = !File.Exists(_applicationPaths.SystemConfigurationFilePath);
-            
+
             Logger = new NLogger("App");
 
             DiscoverTypes();
 
-            _taskManager = new TaskManager(_applicationPaths, _jsonSerializer, Logger);
+            Kernel = new Kernel(this, _applicationPaths, _xmlSerializer, Logger);
+            
+            var networkManager = new NetworkManager();
+
+            var serverManager = new ServerManager(this, Kernel, networkManager, _jsonSerializer, Logger);
+
+            var taskManager = new TaskManager(_applicationPaths, _jsonSerializer, Logger, serverManager);
 
-            Kernel = new Kernel(this, _applicationPaths, _xmlSerializer, _taskManager, Logger);
             ReloadLogger();
 
             Logger.Info("Version {0} initializing", ApplicationVersion);
 
-            _httpServer = ServerFactory.CreateServer(this, ProtobufSerializer, Logger, "Media Browser", "index.html");
+            var httpServer = ServerFactory.CreateServer(this, ProtobufSerializer, Logger, "Media Browser", "index.html");
 
-            RegisterResources();
+            RegisterResources(taskManager, httpServer, networkManager, serverManager);
 
-            FindParts();
+            FindParts(taskManager, httpServer);
         }
 
         /// <summary>
         /// Registers resources that classes will depend on
         /// </summary>
-        private void RegisterResources()
+        private void RegisterResources(ITaskManager taskManager, IHttpServer httpServer, INetworkManager networkManager, IServerManager serverManager)
         {
             RegisterSingleInstance<IKernel>(Kernel);
             RegisterSingleInstance(Kernel);
@@ -121,28 +117,31 @@ namespace MediaBrowser.ServerApplication
 
             RegisterSingleInstance(_applicationPaths);
             RegisterSingleInstance<IApplicationPaths>(_applicationPaths);
-            RegisterSingleInstance(_taskManager);
+            RegisterSingleInstance(taskManager);
             RegisterSingleInstance<IIsoManager>(new PismoIsoManager(Logger));
             RegisterSingleInstance<IBlurayExaminer>(new BdInfoExaminer());
             RegisterSingleInstance<IHttpClient>(new HttpManager(_applicationPaths, Logger));
-            RegisterSingleInstance<INetworkManager>(new NetworkManager());
             RegisterSingleInstance<IZipClient>(new DotNetZipClient());
             RegisterSingleInstance<IWebSocketServer>(() => new AlchemyServer(Logger));
             RegisterSingleInstance(_jsonSerializer);
             RegisterSingleInstance(_xmlSerializer);
             RegisterSingleInstance(ProtobufSerializer);
-            RegisterSingleInstance<IUdpServer>(new UdpServer());
-            RegisterSingleInstance(_httpServer);
+            RegisterSingleInstance<IUdpServer>(new UdpServer(Logger), false);
+            RegisterSingleInstance(httpServer, false);
+
+            RegisterSingleInstance(networkManager);
+
+            RegisterSingleInstance(serverManager);
         }
 
         /// <summary>
         /// Finds the parts.
         /// </summary>
-        private void FindParts()
+        private void FindParts(ITaskManager taskManager, IHttpServer httpServer)
         {
-            _taskManager.AddTasks(GetExports<IScheduledTask>(false));
+            taskManager.AddTasks(GetExports<IScheduledTask>(false));
 
-            _httpServer.Init(GetExports<IRestfulService>(false));
+            httpServer.Init(GetExports<IRestfulService>(false));
         }
 
         /// <summary>
@@ -240,5 +239,13 @@ namespace MediaBrowser.ServerApplication
             // Include composable parts in the running assembly
             yield return GetType().Assembly;
         }
+
+        /// <summary>
+        /// Shuts down.
+        /// </summary>
+        public void Shutdown()
+        {
+            App.Instance.Shutdown();
+        }
     }
 }

+ 0 - 4
MediaBrowser.WebDashboard/Html/css/site.css

@@ -293,10 +293,6 @@ form, .readOnlyContent {
         right: 30px;
     }
 
-    .localnav .ui-btn-inner {
-        font-size: 16px;
-    }
-
     .libraryPage .ui-content {
         padding-right: 50px;
         padding-left: 50px;

+ 1 - 1
Nuget/MediaBrowser.ApiClient.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.ApiClient</id>
-        <version>3.0.0.6-beta</version>
+        <version>3.0.0.7-beta</version>
         <title>MediaBrowser.ApiClient</title>
         <authors>Media Browser Team</authors>
         <owners>scottisafool,Luke</owners>

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.6</version>
+        <version>3.0.7</version>
         <title />
         <authors>Luke</authors>
         <owners>Media Browser Team</owners>
@@ -10,7 +10,7 @@
         <requireLicenseAcceptance>false</requireLicenseAcceptance>
         <description>Contains common components shared by Media Browser Theatre and Media Browser Server. Not intended for plugin developer consumption.</description>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.6" />
+            <dependency id="MediaBrowser.Common" version="3.0.7" />
             <dependency id="NLog" version="2.0.0.2000" />
             <dependency id="ServiceStack" version="3.9.37" />
             <dependency id="ServiceStack.Api.Swagger" version="3.9.35" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.6</version>
+        <version>3.0.7</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners />

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.6</version>
+        <version>3.0.7</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners />
@@ -10,7 +10,7 @@
         <requireLicenseAcceptance>false</requireLicenseAcceptance>
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.6" />
+            <dependency id="MediaBrowser.Common" version="3.0.7" />
         </dependencies>
     </metadata>
     <files>