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.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.News; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Providers; using MediaBrowser.Providers.Manager; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations.BdInfo; using MediaBrowser.Server.Implementations.Configuration; using MediaBrowser.Server.Implementations.Drawing; using MediaBrowser.Server.Implementations.Dto; using MediaBrowser.Server.Implementations.EntryPoints; using MediaBrowser.Server.Implementations.FileOrganization; using MediaBrowser.Server.Implementations.HttpServer; using MediaBrowser.Server.Implementations.IO; using MediaBrowser.Server.Implementations.Library; using MediaBrowser.Server.Implementations.LiveTv; using MediaBrowser.Server.Implementations.Localization; using MediaBrowser.Server.Implementations.MediaEncoder; using MediaBrowser.Server.Implementations.Persistence; using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; using MediaBrowser.Server.Implementations.WebSocket; using MediaBrowser.ServerApplication.EntryPoints; using MediaBrowser.ServerApplication.FFMpeg; using MediaBrowser.ServerApplication.IO; using MediaBrowser.ServerApplication.Native; using MediaBrowser.ServerApplication.Networking; using MediaBrowser.WebDashboard.Api; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.ServerApplication { /// /// Class CompositionRoot /// public class ApplicationHost : BaseApplicationHost, IServerApplicationHost { /// /// Gets the server configuration manager. /// /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager { get { return (IServerConfigurationManager)ConfigurationManager; } } /// /// Gets the name of the web application that can be used for url building. /// All api urls will be of the form {protocol}://{host}:{port}/{appname}/... /// /// The name of the web application. public string WebApplicationName { get { return "mediabrowser"; } } /// /// Gets the HTTP server URL prefix. /// /// The HTTP server URL prefix. private IEnumerable HttpServerUrlPrefixes { get { var list = new List { "http://+:" + ServerConfigurationManager.Configuration.HttpServerPortNumber + "/" + WebApplicationName + "/" }; return list; } } /// /// Gets the configuration manager. /// /// IConfigurationManager. protected override IConfigurationManager GetConfigurationManager() { return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer); } /// /// 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 ILibraryMonitor LibraryMonitor { get; set; } /// /// Gets or sets the provider manager. /// /// The provider manager. private IProviderManager ProviderManager { get; set; } /// /// Gets or sets the HTTP server. /// /// The HTTP server. private IHttpServer HttpServer { get; set; } private IDtoService DtoService { get; set; } private IImageProcessor ImageProcessor { get; set; } /// /// Gets or sets the media encoder. /// /// The media encoder. private IMediaEncoder MediaEncoder { get; set; } private ISessionManager SessionManager { get; set; } private ILiveTvManager LiveTvManager { get; set; } private ILocalizationManager LocalizationManager { get; set; } /// /// Gets or sets the user data repository. /// /// The user data repository. private IUserDataManager UserDataManager { get; set; } private IUserRepository UserRepository { get; set; } internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; } internal IItemRepository ItemRepository { get; set; } private INotificationsRepository NotificationsRepository { get; set; } private IFileOrganizationRepository FileOrganizationRepository { get; set; } private IProviderRepository ProviderRepository { get; set; } /// /// Initializes a new instance of the class. /// /// The application paths. /// The log manager. public ApplicationHost(ServerApplicationPaths applicationPaths, ILogManager logManager, bool isRunningAsService) : base(applicationPaths, logManager) { _isRunningAsService = isRunningAsService; } private readonly bool _isRunningAsService; public override bool IsRunningAsService { get { return _isRunningAsService; } } /// /// Gets the name. /// /// The name. public override string Name { get { return "Media Browser Server"; } } /// /// Gets a value indicating whether this instance can self restart. /// /// true if this instance can self restart; otherwise, false. public override bool CanSelfRestart { get { return NativeApp.CanSelfRestart; } } public bool SupportsAutoRunAtStartup { get { return NativeApp.SupportsAutoRunAtStartup; } } /// /// Runs the startup tasks. /// /// Task. public override async Task RunStartupTasks() { await base.RunStartupTasks().ConfigureAwait(false); Logger.Info("Core startup complete"); Parallel.ForEach(GetExports(), entryPoint => { try { entryPoint.Run(); } catch (Exception ex) { Logger.ErrorException("Error in {0}", ex, entryPoint.GetType().Name); } }); LogManager.RemoveConsoleOutput(); } /// /// Registers resources that classes will depend on /// /// Task. protected override async Task RegisterResources(IProgress progress) { await base.RegisterResources(progress).ConfigureAwait(false); RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer)); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); RegisterSingleInstance(ServerConfigurationManager); RegisterSingleInstance(() => new AlchemyServer(Logger)); RegisterSingleInstance(() => new BdInfoExaminer()); UserDataManager = new UserDataManager(LogManager); RegisterSingleInstance(UserDataManager); UserRepository = await GetUserRepository().ConfigureAwait(false); RegisterSingleInstance(UserRepository); DisplayPreferencesRepository = new SqliteDisplayPreferencesRepository(ApplicationPaths, JsonSerializer, LogManager); RegisterSingleInstance(DisplayPreferencesRepository); ItemRepository = new SqliteItemRepository(ApplicationPaths, JsonSerializer, LogManager); RegisterSingleInstance(ItemRepository); ProviderRepository = new SqliteProviderInfoRepository(ApplicationPaths, LogManager); RegisterSingleInstance(ProviderRepository); FileOrganizationRepository = await GetFileOrganizationRepository().ConfigureAwait(false); RegisterSingleInstance(FileOrganizationRepository); UserManager = new UserManager(Logger, ServerConfigurationManager, UserRepository); RegisterSingleInstance(UserManager); LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager); RegisterSingleInstance(LibraryManager); LibraryMonitor = new LibraryMonitor(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(LibraryMonitor); ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ProviderRepository); RegisterSingleInstance(ProviderManager); RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager); RegisterSingleInstance(SessionManager); HttpServer = ServerFactory.CreateServer(this, LogManager, "Media Browser", "mediabrowser", "dashboard/index.html"); RegisterSingleInstance(HttpServer, false); progress.Report(10); ServerManager = new ServerManager(this, JsonSerializer, Logger, ServerConfigurationManager); RegisterSingleInstance(ServerManager); LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(LocalizationManager); ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer); RegisterSingleInstance(ImageProcessor); DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager); RegisterSingleInstance(DtoService); var newsService = new Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); RegisterSingleInstance(newsService); var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, Logger, LibraryMonitor, LibraryManager, ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(fileOrganizationService); progress.Report(15); var innerProgress = new ActionableProgress(); innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); progress.Report(90); LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, MediaEncoder, TaskManager); RegisterSingleInstance(LiveTvManager); 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)); await ConfigureNotificationsRepository().ConfigureAwait(false); progress.Report(92); await Task.WhenAll(itemsTask, displayPreferencesTask, userdataTask).ConfigureAwait(false); progress.Report(100); await ((UserManager)UserManager).Initialize().ConfigureAwait(false); SetKernelProperties(); } protected override INetworkManager CreateNetworkManager() { return new NetworkManager(); } protected override IFileSystem CreateFileSystemManager() { return FileSystemFactory.CreateFileSystemManager(LogManager); } /// /// Registers the media encoder. /// /// Task. private async Task RegisterMediaEncoder(IProgress progress) { var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager).GetFFMpegInfo(progress).ConfigureAwait(false); MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ApplicationPaths, JsonSerializer, info.Path, info.ProbePath, info.Version, FileSystemManager); RegisterSingleInstance(MediaEncoder); } /// /// Sets the kernel properties. /// private void SetKernelProperties() { new FFMpegManager(MediaEncoder, Logger, ItemRepository, FileSystemManager, ServerConfigurationManager); LocalizedStrings.StringFiles = GetExports(); SetStaticProperties(); } /// /// Gets the user repository. /// /// Task{IUserRepository}. private async Task GetUserRepository() { var repo = new SqliteUserRepository(JsonSerializer, LogManager, ApplicationPaths); await repo.Initialize().ConfigureAwait(false); return repo; } /// /// Gets the file organization repository. /// /// Task{IUserRepository}. private async Task GetFileOrganizationRepository() { var repo = new SqliteFileOrganizationRepository(LogManager, ServerConfigurationManager.ApplicationPaths); await repo.Initialize().ConfigureAwait(false); return repo; } /// /// Configures the repositories. /// /// Task. private async Task ConfigureNotificationsRepository() { var repo = new SqliteNotificationsRepository(LogManager, ApplicationPaths); await repo.Initialize().ConfigureAwait(false); NotificationsRepository = repo; RegisterSingleInstance(NotificationsRepository); } /// /// Configures the repositories. /// /// Task. private async Task ConfigureDisplayPreferencesRepositories() { await DisplayPreferencesRepository.Initialize().ConfigureAwait(false); } /// /// Configures the item repositories. /// /// Task. private async Task ConfigureItemRepositories() { await ItemRepository.Initialize().ConfigureAwait(false); await ProviderRepository.Initialize().ConfigureAwait(false); ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; } /// /// Configures the user data repositories. /// /// Task. private async Task ConfigureUserDataRepositories() { var repo = new SqliteUserDataRepository(ApplicationPaths, LogManager); await repo.Initialize().ConfigureAwait(false); ((UserDataManager)UserDataManager).Repository = repo; } /// /// 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; BaseItem.LocalizationManager = LocalizationManager; BaseItem.ItemRepository = ItemRepository; User.XmlSerializer = XmlSerializer; User.UserManager = UserManager; LocalizedStrings.ApplicationPaths = ApplicationPaths; Folder.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; } /// /// Finds the parts. /// protected override void FindParts() { if (IsFirstRun) { RegisterServerWithAdministratorAccess(); } base.FindParts(); HttpServer.Init(GetExports(false)); ServerManager.AddWebSocketListeners(GetExports(false)); StartServer(true); LibraryManager.AddParts(GetExports(), GetExports(), GetExports(), GetExports(), GetExports(), GetExports(), GetExports(), GetExports()); ProviderManager.AddParts(GetExports(), GetExports(), GetExports(), GetExports(), GetExports()); ImageProcessor.AddParts(GetExports()); LiveTvManager.AddParts(GetExports()); SessionManager.AddParts(GetExports()); } /// /// Starts the server. /// /// if set to true [retry on failure]. private void StartServer(bool retryOnFailure) { try { ServerManager.Start(HttpServerUrlPrefixes, ServerConfigurationManager.Configuration.EnableHttpLevelLogging); } catch (Exception ex) { Logger.ErrorException("Error starting http server", ex); if (retryOnFailure) { RegisterServerWithAdministratorAccess(); StartServer(false); } else { throw; } } ServerManager.StartWebSocketServer(); } /// /// Called when [configuration updated]. /// /// The sender. /// The instance containing the event data. protected override void OnConfigurationUpdated(object sender, EventArgs e) { base.OnConfigurationUpdated(sender, e); HttpServer.EnableHttpRequestLogging = ServerConfigurationManager.Configuration.EnableHttpLevelLogging; if (!HttpServer.UrlPrefixes.SequenceEqual(HttpServerUrlPrefixes, StringComparer.OrdinalIgnoreCase)) { NotifyPendingRestart(); } else if (!ServerManager.SupportsNativeWebSocket && ServerManager.WebSocketPortNumber != ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber) { NotifyPendingRestart(); } } /// /// Restarts this instance. /// public override async Task Restart() { if (!CanSelfRestart) { throw new InvalidOperationException("The server is unable to self-restart. Please restart manually."); } try { await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { Logger.ErrorException("Error sending server restart notification", ex); } NativeApp.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 { #if DEBUG return false; #endif return NativeApp.CanSelfUpdate; } } /// /// Gets the composable part assemblies. /// /// IEnumerable{Assembly}. protected override IEnumerable GetComposablePartAssemblies() { var list = GetPluginAssemblies() .ToList(); // 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 // Include composable parts in the Api assembly list.Add(typeof(ApiEntryPoint).Assembly); // Include composable parts in the Dashboard assembly list.Add(typeof(DashboardInfo).Assembly); // Include composable parts in the Model assembly list.Add(typeof(SystemInfo).Assembly); // Include composable parts in the Common assembly list.Add(typeof(IApplicationHost).Assembly); // Include composable parts in the Controller assembly list.Add(typeof(IServerApplicationHost).Assembly); // Include composable parts in the Providers assembly list.Add(typeof(ImagesByNameProvider).Assembly); // Common implementations list.Add(typeof(TaskManager).Assembly); // Server implementations list.Add(typeof(ServerApplicationPaths).Assembly); list.AddRange(Assemblies.GetAssembliesWithParts()); // Include composable parts in the running assembly list.Add(GetType().Assembly); return list; } /// /// Gets the plugin assemblies. /// /// IEnumerable{Assembly}. private IEnumerable GetPluginAssemblies() { try { return Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) .Select(LoadAssembly) .Where(a => a != null) .ToList(); } catch (DirectoryNotFoundException) { return new List(); } } private readonly string _systemId = Environment.MachineName.GetMD5().ToString(); /// /// 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.ToList(), InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToList(), CompletedInstallations = InstallationManager.CompletedInstallations.ToList(), Id = _systemId, ProgramDataPath = ApplicationPaths.ProgramDataPath, LogPath = ApplicationPaths.LogDirectoryPath, ItemsByNamePath = ApplicationPaths.ItemsByNamePath, CachePath = ApplicationPaths.CachePath, MacAddress = GetMacAddress(), HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber, OperatingSystem = Environment.OSVersion.ToString(), CanSelfRestart = CanSelfRestart, CanSelfUpdate = CanSelfUpdate, WanAddress = GetWanAddress(), HasUpdateAvailable = _hasUpdateAvailable, SupportsAutoRunAtStartup = SupportsAutoRunAtStartup, TranscodingTempPath = ApplicationPaths.TranscodingTempPath, IsRunningAsService = IsRunningAsService }; } private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private string GetWanAddress() { var ip = WanAddressEntryPoint.WanAddress; if (!string.IsNullOrEmpty(ip)) { return "http://" + ip + ":" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(_usCulture); } return null; } /// /// Gets the mac address. /// /// System.String. private string GetMacAddress() { try { return NetworkManager.GetMacAddress(); } catch (Exception ex) { Logger.ErrorException("Error getting mac address", ex); return null; } } /// /// Shuts down. /// public override async Task Shutdown() { try { await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { Logger.ErrorException("Error sending server shutdown notification", ex); } NativeApp.Shutdown(); } /// /// Registers the server with administrator access. /// private void RegisterServerWithAdministratorAccess() { Logger.Info("Requesting administrative access to authorize http server"); try { ServerAuthorization.AuthorizeServer( ServerConfigurationManager.Configuration.HttpServerPortNumber, HttpServerUrlPrefixes.First(), ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber, UdpServerEntryPoint.PortNumber, ConfigurationManager.CommonApplicationPaths.TempDirectory); } catch (Exception ex) { Logger.ErrorException("Error authorizing server", ex); } } private bool _hasUpdateAvailable; /// /// Checks for update. /// /// The cancellation token. /// The progress. /// Task{CheckForUpdateResult}. public override async Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) { var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false); var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, Constants.MbServerPkgName, null, ApplicationVersion, ConfigurationManager.CommonConfiguration.SystemUpdateLevel); _hasUpdateAvailable = version != null; return version != null ? new CheckForUpdateResult { AvailableVersion = version.version, IsUpdateAvailable = version.version > ApplicationVersion, Package = version } : new CheckForUpdateResult { AvailableVersion = ApplicationVersion, IsUpdateAvailable = false }; } /// /// Updates the application. /// /// The package that contains the update /// The cancellation token. /// The progress. /// Task. public override async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress progress) { await InstallationManager.InstallPackage(package, progress, cancellationToken).ConfigureAwait(false); _hasUpdateAvailable = false; OnApplicationUpdated(package.version); } /// /// Configures the automatic run at startup. /// /// if set to true [autorun]. protected override void ConfigureAutoRunAtStartup(bool autorun) { if (SupportsAutoRunAtStartup) { Autorun.Configure(autorun); } } } }