using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Constants; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Implementations; using MediaBrowser.Common.Implementations.ScheduledTasks; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Updates; using MediaBrowser.Controller.Weather; using MediaBrowser.IsoMounter; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations.BdInfo; using MediaBrowser.Server.Implementations.Configuration; using MediaBrowser.Server.Implementations.HttpServer; using MediaBrowser.Server.Implementations.IO; using MediaBrowser.Server.Implementations.Library; using MediaBrowser.Server.Implementations.MediaEncoder; using MediaBrowser.Server.Implementations.Providers; using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Udp; using MediaBrowser.Server.Implementations.Updates; using MediaBrowser.Server.Implementations.WebSocket; using MediaBrowser.ServerApplication.Implementations; using MediaBrowser.WebDashboard.Api; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Sockets; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.ServerApplication { /// /// Class CompositionRoot /// public class ApplicationHost : BaseApplicationHost, IServerApplicationHost { private const int UdpServerPort = 7359; /// /// Gets the server kernel. /// /// The server kernel. protected Kernel ServerKernel { get; set; } /// /// Gets the server configuration manager. /// /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager { get { return (IServerConfigurationManager)ConfigurationManager; } } /// /// Gets the name of the log file prefix. /// /// The name of the log file prefix. protected override string LogFilePrefixName { get { return "Server"; } } /// /// Gets the configuration manager. /// /// IConfigurationManager. protected override IConfigurationManager GetConfigurationManager() { return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer); } /// /// Gets or sets the installation manager. /// /// The installation manager. private IInstallationManager InstallationManager { get; set; } /// /// Gets or sets the server manager. /// /// The server manager. private IServerManager ServerManager { get; set; } /// /// Gets or sets the user manager. /// /// The user manager. public IUserManager UserManager { get; set; } /// /// Gets or sets the library manager. /// /// The library manager. internal ILibraryManager LibraryManager { get; set; } /// /// Gets or sets the directory watchers. /// /// The directory watchers. private IDirectoryWatchers DirectoryWatchers { get; set; } /// /// Gets or sets the provider manager. /// /// The provider manager. private IProviderManager ProviderManager { get; set; } /// /// Gets or sets the zip client. /// /// The zip client. private IZipClient ZipClient { get; set; } /// /// Gets or sets the HTTP server. /// /// The HTTP server. private IHttpServer HttpServer { get; set; } /// /// Gets or sets the UDP server. /// /// The UDP server. private UdpServer UdpServer { get; set; } /// /// Gets or sets the display preferences manager. /// /// The display preferences manager. internal IDisplayPreferencesManager DisplayPreferencesManager { get; set; } /// /// Gets or sets the media encoder. /// /// The media encoder. private IMediaEncoder MediaEncoder { get; set; } /// /// The full path to our startmenu shortcut /// protected override string ProductShortcutPath { get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk"); } } private Task _httpServerCreationTask; /// /// Runs the startup tasks. /// /// Task. protected override async Task RunStartupTasks() { await base.RunStartupTasks().ConfigureAwait(false); DirectoryWatchers.Start(); Logger.Info("Core startup complete"); Parallel.ForEach(GetExports(), entryPoint => entryPoint.Run()); } /// /// Called when [logger loaded]. /// protected override void OnLoggerLoaded() { base.OnLoggerLoaded(); _httpServerCreationTask = Task.Run(() => ServerFactory.CreateServer(this, Logger, "Media Browser", "index.html")); } /// /// Registers resources that classes will depend on /// /// Task. protected override async Task RegisterResources() { ServerKernel = new Kernel(ServerConfigurationManager); await base.RegisterResources().ConfigureAwait(false); RegisterSingleInstance(new HttpResultFactory(LogManager)); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); RegisterSingleInstance(ServerKernel); RegisterSingleInstance(ServerConfigurationManager); RegisterSingleInstance(() => new AlchemyServer(Logger)); RegisterSingleInstance(() => new PismoIsoManager(Logger)); RegisterSingleInstance(() => new BdInfoExaminer()); ZipClient = new DotNetZipClient(); RegisterSingleInstance(ZipClient); UserManager = new UserManager(Logger, ServerConfigurationManager); RegisterSingleInstance(UserManager); LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(LibraryManager); InstallationManager = new InstallationManager(HttpClient, PackageManager, JsonSerializer, Logger, this); RegisterSingleInstance(InstallationManager); DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager); RegisterSingleInstance(DirectoryWatchers); ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager); RegisterSingleInstance(ProviderManager); DisplayPreferencesManager = new DisplayPreferencesManager(LogManager.GetLogger("DisplayPreferencesManager")); RegisterSingleInstance(DisplayPreferencesManager); RegisterSingleInstance(() => new LuceneSearchEngine()); MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ZipClient, ApplicationPaths, JsonSerializer); RegisterSingleInstance(MediaEncoder); HttpServer = await _httpServerCreationTask.ConfigureAwait(false); RegisterSingleInstance(HttpServer, false); ServerManager = new ServerManager(this, JsonSerializer, Logger, ServerConfigurationManager, ServerKernel); RegisterSingleInstance(ServerManager); var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false)); var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false)); var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false)); var userTask = Task.Run(async () => await ConfigureUserRepositories().ConfigureAwait(false)); await Task.WhenAll(itemsTask, userTask, displayPreferencesTask, userdataTask).ConfigureAwait(false); SetKernelProperties(); } /// /// Sets the kernel properties. /// private void SetKernelProperties() { Parallel.Invoke( () => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, LibraryManager), () => ServerKernel.ImageManager = new ImageManager(ServerKernel, LogManager.GetLogger("ImageManager"), ApplicationPaths), () => ServerKernel.WeatherProviders = GetExports(), () => ServerKernel.ImageEnhancers = GetExports().OrderBy(e => e.Priority).ToArray(), () => ServerKernel.StringFiles = GetExports(), SetStaticProperties ); } /// /// Configures the repositories. /// /// Task. private async Task ConfigureDisplayPreferencesRepositories() { var repositories = GetExports(); var repository = GetRepository(repositories, ServerConfigurationManager.Configuration.DisplayPreferencesRepository); await repository.Initialize().ConfigureAwait(false); ((DisplayPreferencesManager)DisplayPreferencesManager).Repository = repository; } /// /// Configures the item repositories. /// /// Task. private async Task ConfigureItemRepositories() { var repositories = GetExports(); var repository = GetRepository(repositories, ServerConfigurationManager.Configuration.ItemRepository); await repository.Initialize().ConfigureAwait(false); ((LibraryManager)LibraryManager).ItemRepository = repository; } /// /// Configures the user data repositories. /// /// Task. private async Task ConfigureUserDataRepositories() { var repositories = GetExports(); var repository = GetRepository(repositories, ServerConfigurationManager.Configuration.UserDataRepository); await repository.Initialize().ConfigureAwait(false); ((UserManager)UserManager).UserDataRepository = repository; } private async Task ConfigureUserRepositories() { var repositories = GetExports(); var repository = GetRepository(repositories, ServerConfigurationManager.Configuration.UserRepository); await repository.Initialize().ConfigureAwait(false); ((UserManager)UserManager).UserRepository = repository; } /// /// Dirty hacks /// private void SetStaticProperties() { // For now there's no real way to inject these properly BaseItem.Logger = LogManager.GetLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = LibraryManager; BaseItem.ProviderManager = ProviderManager; User.XmlSerializer = XmlSerializer; User.UserManager = UserManager; Ratings.ConfigurationManager = ServerConfigurationManager; LocalizedStrings.ApplicationPaths = ApplicationPaths; } /// /// Finds the parts. /// protected override void FindParts() { if (IsFirstRun) { RegisterServerWithAdministratorAccess(); } Parallel.Invoke( () => base.FindParts(), () => { HttpServer.Init(GetExports(false)); ServerManager.AddWebSocketListeners(GetExports(false)); ServerManager.Start(); }, () => LibraryManager.AddParts(GetExports(), GetExports(), GetExports(), GetExports(), GetExports()), () => ProviderManager.AddMetadataProviders(GetExports().ToArray()), () => { UdpServer = new UdpServer(Logger, NetworkManager, ServerConfigurationManager); try { UdpServer.Start(UdpServerPort); } catch (SocketException ex) { Logger.ErrorException("Failed to start UDP Server", ex); } } ); } /// /// Restarts this instance. /// public override void Restart() { App.Instance.Restart(); } /// /// Gets or sets a value indicating whether this instance can self update. /// /// true if this instance can self update; otherwise, false. public override bool CanSelfUpdate { get { return ConfigurationManager.CommonConfiguration.EnableAutoUpdate; } } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool dispose) { if (dispose) { if (UdpServer != null) { UdpServer.Dispose(); } } base.Dispose(dispose); } /// /// Checks for update. /// /// The cancellation token. /// The progress. /// Task{CheckForUpdateResult}. public async override Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) { var availablePackages = await PackageManager.GetAvailablePackages(CancellationToken.None).ConfigureAwait(false); var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, Constants.MbServerPkgName, ConfigurationManager.CommonConfiguration.SystemUpdateLevel); return version != null ? new CheckForUpdateResult { AvailableVersion = version.version, IsUpdateAvailable = version.version > ApplicationVersion, Package = version } : new CheckForUpdateResult { AvailableVersion = ApplicationVersion, IsUpdateAvailable = false }; } /// /// Gets the composable part assemblies. /// /// IEnumerable{Assembly}. protected override IEnumerable GetComposablePartAssemblies() { // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that // This will prevent the .dll file from getting locked, and allow us to replace it when needed foreach (var pluginAssembly in Directory .EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) .Select(LoadAssembly).Where(a => a != null)) { yield return pluginAssembly; } // Include composable parts in the Api assembly yield return typeof(ApiEntryPoint).Assembly; // Include composable parts in the Dashboard assembly yield return typeof(DashboardInfo).Assembly; // Include composable parts in the Model assembly yield return typeof(SystemInfo).Assembly; // Include composable parts in the Common assembly yield return typeof(IApplicationHost).Assembly; // Include composable parts in the Controller assembly yield return typeof(Kernel).Assembly; // Common implementations yield return typeof(TaskManager).Assembly; // Server implementations yield return typeof(ServerApplicationPaths).Assembly; // Include composable parts in the running assembly yield return GetType().Assembly; } private readonly Guid _systemId = Environment.MachineName.GetMD5(); /// /// Gets the system status. /// /// SystemInfo. public virtual SystemInfo GetSystemInfo() { return new SystemInfo { HasPendingRestart = HasPendingRestart, Version = ApplicationVersion.ToString(), IsNetworkDeployed = CanSelfUpdate, WebSocketPortNumber = ServerManager.WebSocketPortNumber, SupportsNativeWebSocket = ServerManager.SupportsNativeWebSocket, FailedPluginAssemblies = FailedAssemblies.ToArray(), InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToArray(), CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(), Id = _systemId }; } /// /// Shuts down. /// public override void Shutdown() { App.Instance.Dispatcher.Invoke(App.Instance.Shutdown); } /// /// Registers the server with administrator access. /// private void RegisterServerWithAdministratorAccess() { Logger.Info("Requesting administrative access to authorize http server"); // Create a temp file path to extract the bat file to var tmpFile = Path.Combine(ConfigurationManager.CommonApplicationPaths.TempDirectory, Guid.NewGuid() + ".bat"); // Extract the bat file using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.ServerApplication.RegisterServer.bat")) { using (var fileStream = File.Create(tmpFile)) { stream.CopyTo(fileStream); } } var startInfo = new ProcessStartInfo { FileName = tmpFile, Arguments = string.Format("{0} {1} {2} {3}", ServerConfigurationManager.Configuration.HttpServerPortNumber, ServerKernel.HttpServerUrlPrefix, UdpServerPort, ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber), CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, Verb = "runas", ErrorDialog = false }; using (var process = Process.Start(startInfo)) { process.WaitForExit(); } } /// /// Gets the repository. /// /// /// The repositories. /// The name. /// ``0. private T GetRepository(IEnumerable repositories, string name) where T : class, IRepository { var enumerable = repositories as T[] ?? repositories.ToArray(); return enumerable.FirstOrDefault(r => string.Equals(r.Name, name, StringComparison.OrdinalIgnoreCase)) ?? enumerable.FirstOrDefault(); } } }