diff options
| author | hatharry <hatharry@hotmail.com> | 2016-07-25 23:29:52 +1200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-07-25 23:29:52 +1200 |
| commit | f21f9923de6291aaf985f32dbbbaddbb26d07fb1 (patch) | |
| tree | 1a313e9a1c6790a755926bcef221c5f680537eae /MediaBrowser.Server.Startup.Common | |
| parent | 6332d0b9436c511a59e2abd67ea8c24ce3d82ace (diff) | |
| parent | 8328f39834f042e1808fd8506bbc7c48151703ab (diff) | |
Merge pull request #15 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.Server.Startup.Common')
17 files changed, 606 insertions, 999 deletions
diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index dd7e3cc01..b7ea5bdad 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -160,7 +160,6 @@ namespace MediaBrowser.Server.Startup.Common private IHttpServer HttpServer { get; set; } private IDtoService DtoService { get; set; } private IImageProcessor ImageProcessor { get; set; } - private ISeriesOrderManager SeriesOrderManager { get; set; } /// <summary> /// Gets or sets the media encoder. @@ -190,7 +189,6 @@ namespace MediaBrowser.Server.Startup.Common internal IItemRepository ItemRepository { get; set; } private INotificationsRepository NotificationsRepository { get; set; } private IFileOrganizationRepository FileOrganizationRepository { get; set; } - private IProviderRepository ProviderRepository { get; set; } private INotificationManager NotificationManager { get; set; } private ISubtitleManager SubtitleManager { get; set; } @@ -276,7 +274,7 @@ namespace MediaBrowser.Server.Startup.Common { get { - return "Media Browser Server"; + return "Emby Server"; } } @@ -314,7 +312,6 @@ namespace MediaBrowser.Server.Startup.Common /// <summary> /// Runs the startup tasks. /// </summary> - /// <returns>Task.</returns> public override async Task RunStartupTasks() { if (ServerConfigurationManager.Configuration.MigrationVersion < CleanDatabaseScheduledTask.MigrationVersion && @@ -325,23 +322,30 @@ namespace MediaBrowser.Server.Startup.Common await base.RunStartupTasks().ConfigureAwait(false); + await MediaEncoder.Init().ConfigureAwait(false); + Logger.Info("ServerId: {0}", SystemId); Logger.Info("Core startup complete"); HttpServer.GlobalResponse = null; PerformPostInitMigrations(); + Logger.Info("Post-init migrations complete"); - Parallel.ForEach(GetExports<IServerEntryPoint>(), entryPoint => + foreach (var entryPoint in GetExports<IServerEntryPoint>().ToList()) { + var name = entryPoint.GetType().FullName; + Logger.Info("Starting entry point {0}", name); try { entryPoint.Run(); } catch (Exception ex) { - Logger.ErrorException("Error in {0}", ex, entryPoint.GetType().Name); + Logger.ErrorException("Error in {0}", ex, name); } - }); + Logger.Info("Entry point completed: {0}", name); + } + Logger.Info("All entry points have started"); LogManager.RemoveConsoleOutput(); } @@ -360,12 +364,18 @@ namespace MediaBrowser.Server.Startup.Common { var migrations = new List<IVersionMigration> { - new RenameXmlOptions(ServerConfigurationManager) }; foreach (var task in migrations) { - task.Run(); + try + { + task.Run(); + } + catch (Exception ex) + { + Logger.ErrorException("Error running migration", ex); + } } } @@ -375,19 +385,28 @@ namespace MediaBrowser.Server.Startup.Common { new OmdbEpisodeProviderMigration(ServerConfigurationManager), new MovieDbEpisodeProviderMigration(ServerConfigurationManager), - new DbMigration(ServerConfigurationManager, TaskManager) + new DbMigration(ServerConfigurationManager, TaskManager), + new FolderViewSettingMigration(ServerConfigurationManager, UserManager), + new CollectionGroupingMigration(ServerConfigurationManager, UserManager), + new CollectionsViewMigration(ServerConfigurationManager, UserManager) }; foreach (var task in migrations) { - task.Run(); + try + { + task.Run(); + } + catch (Exception ex) + { + Logger.ErrorException("Error running migration", ex); + } } } /// <summary> /// Registers resources that classes will depend on /// </summary> - /// <returns>Task.</returns> protected override async Task RegisterResources(IProgress<double> progress) { await base.RegisterResources(progress).ConfigureAwait(false); @@ -399,7 +418,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(ServerConfigurationManager); - LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer); + LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LogManager.GetLogger("LocalizationManager")); RegisterSingleInstance(LocalizationManager); RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer()); @@ -410,15 +429,14 @@ namespace MediaBrowser.Server.Startup.Common UserRepository = await GetUserRepository().ConfigureAwait(false); RegisterSingleInstance(UserRepository); - DisplayPreferencesRepository = new SqliteDisplayPreferencesRepository(LogManager, JsonSerializer, ApplicationPaths); + var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager, JsonSerializer, ApplicationPaths, NativeApp.GetDbConnector()); + DisplayPreferencesRepository = displayPreferencesRepo; RegisterSingleInstance(DisplayPreferencesRepository); - ItemRepository = new SqliteItemRepository(ApplicationPaths, JsonSerializer, LogManager); + var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager, NativeApp.GetDbConnector()); + ItemRepository = itemRepo; RegisterSingleInstance(ItemRepository); - ProviderRepository = new SqliteProviderInfoRepository(LogManager, ApplicationPaths); - RegisterSingleInstance(ProviderRepository); - FileOrganizationRepository = await GetFileOrganizationRepository().ConfigureAwait(false); RegisterSingleInstance(FileOrganizationRepository); @@ -440,12 +458,9 @@ namespace MediaBrowser.Server.Startup.Common LibraryMonitor = new LibraryMonitor(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, this); RegisterSingleInstance(LibraryMonitor); - ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager); + ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); RegisterSingleInstance(ProviderManager); - SeriesOrderManager = new SeriesOrderManager(); - RegisterSingleInstance(SeriesOrderManager); - RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager)); HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, "Emby", "web/index.html"); @@ -462,7 +477,7 @@ namespace MediaBrowser.Server.Startup.Common ImageProcessor = GetImageProcessor(); RegisterSingleInstance(ImageProcessor); - TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager); + TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); RegisterSingleInstance(TVSeriesManager); SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder, ServerConfigurationManager, UserDataManager, () => MediaSourceManager, JsonSerializer, TaskManager); @@ -474,7 +489,7 @@ namespace MediaBrowser.Server.Startup.Common var encryptionManager = new EncryptionManager(); RegisterSingleInstance<IEncryptionManager>(encryptionManager); - ConnectManager = new ConnectManager(LogManager.GetLogger("Connect"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager, FileSystemManager); + ConnectManager = new ConnectManager(LogManager.GetLogger("ConnectManager"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager, FileSystemManager); RegisterSingleInstance(ConnectManager); DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); @@ -515,7 +530,7 @@ namespace MediaBrowser.Server.Startup.Common UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); RegisterSingleInstance(UserViewManager); - var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager); + var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder); RegisterSingleInstance<IContentDirectory>(contentDirectory); var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager); @@ -540,7 +555,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance(NativeApp.GetPowerManagement()); - var sharingRepo = new SharingRepository(LogManager, ApplicationPaths); + var sharingRepo = new SharingRepository(LogManager, ApplicationPaths, NativeApp.GetDbConnector()); await sharingRepo.Initialize().ConfigureAwait(false); RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this)); @@ -558,9 +573,13 @@ namespace MediaBrowser.Server.Startup.Common SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager); RegisterSingleInstance(SubtitleEncoder); - await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false); - await ConfigureItemRepositories().ConfigureAwait(false); - await ConfigureUserDataRepositories().ConfigureAwait(false); + await displayPreferencesRepo.Initialize().ConfigureAwait(false); + + var userDataRepo = new SqliteUserDataRepository(LogManager, ApplicationPaths, NativeApp.GetDbConnector()); + + ((UserDataManager)UserDataManager).Repository = userDataRepo; + await itemRepo.Initialize(userDataRepo).ConfigureAwait(false); + ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; await ConfigureNotificationsRepository().ConfigureAwait(false); progress.Report(100); @@ -618,14 +637,21 @@ namespace MediaBrowser.Server.Startup.Common /// <returns>Task.</returns> private async Task RegisterMediaEncoder(IProgress<double> progress) { - var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment) + string encoderPath = null; + string probePath = null; + + var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment, NativeApp.GetFfmpegInstallInfo()) .GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false); + encoderPath = info.EncoderPath; + probePath = info.ProbePath; + var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase); + var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), JsonSerializer, - info.EncoderPath, - info.ProbePath, - info.Version, + encoderPath, + probePath, + hasExternalEncoder, ServerConfigurationManager, FileSystemManager, LiveTvManager, @@ -634,18 +660,12 @@ namespace MediaBrowser.Server.Startup.Common ChannelManager, SessionManager, () => SubtitleEncoder, - () => MediaSourceManager); + () => MediaSourceManager, + HttpClient, + ZipClient); MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); - - Task.Run(() => - { - var result = new FFmpegValidator(Logger, ApplicationPaths, FileSystemManager).Validate(info); - - mediaEncoder.SetAvailableDecoders(result.Item1); - mediaEncoder.SetAvailableEncoders(result.Item2); - }); } /// <summary> @@ -656,7 +676,7 @@ namespace MediaBrowser.Server.Startup.Common { try { - var repo = new SqliteUserRepository(LogManager, ApplicationPaths, JsonSerializer); + var repo = new SqliteUserRepository(LogManager, ApplicationPaths, JsonSerializer, NativeApp.GetDbConnector()); await repo.Initialize().ConfigureAwait(false); @@ -675,7 +695,7 @@ namespace MediaBrowser.Server.Startup.Common /// <returns>Task{IUserRepository}.</returns> private async Task<IFileOrganizationRepository> GetFileOrganizationRepository() { - var repo = new SqliteFileOrganizationRepository(LogManager, ServerConfigurationManager.ApplicationPaths); + var repo = new SqliteFileOrganizationRepository(LogManager, ServerConfigurationManager.ApplicationPaths, NativeApp.GetDbConnector()); await repo.Initialize().ConfigureAwait(false); @@ -684,7 +704,7 @@ namespace MediaBrowser.Server.Startup.Common private async Task<IAuthenticationRepository> GetAuthenticationRepository() { - var repo = new AuthenticationRepository(LogManager, ServerConfigurationManager.ApplicationPaths); + var repo = new AuthenticationRepository(LogManager, ServerConfigurationManager.ApplicationPaths, NativeApp.GetDbConnector()); await repo.Initialize().ConfigureAwait(false); @@ -693,7 +713,7 @@ namespace MediaBrowser.Server.Startup.Common private async Task<IActivityRepository> GetActivityLogRepository() { - var repo = new ActivityRepository(LogManager, ServerConfigurationManager.ApplicationPaths); + var repo = new ActivityRepository(LogManager, ServerConfigurationManager.ApplicationPaths, NativeApp.GetDbConnector()); await repo.Initialize().ConfigureAwait(false); @@ -702,7 +722,7 @@ namespace MediaBrowser.Server.Startup.Common private async Task<ISyncRepository> GetSyncRepository() { - var repo = new SyncRepository(LogManager, JsonSerializer, ServerConfigurationManager.ApplicationPaths); + var repo = new SyncRepository(LogManager, JsonSerializer, ServerConfigurationManager.ApplicationPaths, NativeApp.GetDbConnector()); await repo.Initialize().ConfigureAwait(false); @@ -712,10 +732,9 @@ namespace MediaBrowser.Server.Startup.Common /// <summary> /// Configures the repositories. /// </summary> - /// <returns>Task.</returns> private async Task ConfigureNotificationsRepository() { - var repo = new SqliteNotificationsRepository(LogManager, ApplicationPaths); + var repo = new SqliteNotificationsRepository(LogManager, ApplicationPaths, NativeApp.GetDbConnector()); await repo.Initialize().ConfigureAwait(false); @@ -725,41 +744,6 @@ namespace MediaBrowser.Server.Startup.Common } /// <summary> - /// Configures the repositories. - /// </summary> - /// <returns>Task.</returns> - private async Task ConfigureDisplayPreferencesRepositories() - { - await DisplayPreferencesRepository.Initialize().ConfigureAwait(false); - } - - /// <summary> - /// Configures the item repositories. - /// </summary> - /// <returns>Task.</returns> - private async Task ConfigureItemRepositories() - { - await ItemRepository.Initialize().ConfigureAwait(false); - - await ProviderRepository.Initialize().ConfigureAwait(false); - - ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; - } - - /// <summary> - /// Configures the user data repositories. - /// </summary> - /// <returns>Task.</returns> - private async Task ConfigureUserDataRepositories() - { - var repo = new SqliteUserDataRepository(LogManager, ApplicationPaths); - - await repo.Initialize().ConfigureAwait(false); - - ((UserDataManager)UserDataManager).Repository = repo; - } - - /// <summary> /// Dirty hacks /// </summary> private void SetStaticProperties() @@ -814,15 +798,11 @@ namespace MediaBrowser.Server.Startup.Common ProviderManager.AddParts(GetExports<IImageProvider>(), GetExports<IMetadataService>(), - GetExports<IItemIdentityProvider>(), - GetExports<IItemIdentityConverter>(), GetExports<IMetadataProvider>(), GetExports<IMetadataSaver>(), GetExports<IImageSaver>(), GetExports<IExternalId>()); - SeriesOrderManager.AddParts(GetExports<ISeriesOrderProvider>()); - ImageProcessor.AddParts(GetExports<IImageEnhancer>()); LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>()); @@ -842,19 +822,57 @@ namespace MediaBrowser.Server.Startup.Common private string CertificatePath { get; set; } + private string NormalizeConfiguredLocalAddress(string address) + { + var index = address.Trim('/').IndexOf('/'); + + if (index != -1) + { + address = address.Substring(index + 1); + } + + return address.Trim('/'); + } private IEnumerable<string> GetUrlPrefixes() { - var prefixes = new List<string> - { - "http://+:" + ServerConfigurationManager.Configuration.HttpServerPortNumber + "/" - }; + var hosts = ServerConfigurationManager + .Configuration + .LocalNetworkAddresses + .Select(NormalizeConfiguredLocalAddress) + .ToList(); + + if (hosts.Count == 0) + { + hosts.Add("+"); + } - if (!string.IsNullOrWhiteSpace(CertificatePath)) + if (!hosts.Contains("+", StringComparer.OrdinalIgnoreCase)) { - prefixes.Add("https://+:" + ServerConfigurationManager.Configuration.HttpsPortNumber + "/"); + if (!hosts.Contains("localhost", StringComparer.OrdinalIgnoreCase)) + { + hosts.Add("localhost"); + } + + if (!hosts.Contains("127.0.0.1", StringComparer.OrdinalIgnoreCase)) + { + hosts.Add("127.0.0.1"); + } } - return prefixes; + return hosts.SelectMany(i => + { + var prefixes = new List<string> + { + "http://"+i+":" + ServerConfigurationManager.Configuration.HttpServerPortNumber + "/" + }; + + if (!string.IsNullOrWhiteSpace(CertificatePath)) + { + prefixes.Add("https://" + i + ":" + ServerConfigurationManager.Configuration.HttpsPortNumber + "/"); + } + + return prefixes; + }); } /// <summary> @@ -1073,8 +1091,10 @@ namespace MediaBrowser.Server.Startup.Common /// Gets the system status. /// </summary> /// <returns>SystemInfo.</returns> - public virtual SystemInfo GetSystemInfo() + public async Task<SystemInfo> GetSystemInfo() { + var localAddress = await GetLocalApiUrl().ConfigureAwait(false); + return new SystemInfo { HasPendingRestart = HasPendingRestart, @@ -1105,8 +1125,10 @@ namespace MediaBrowser.Server.Startup.Common IsRunningAsService = IsRunningAsService, SupportsRunningAsService = SupportsRunningAsService, ServerName = FriendlyName, - LocalAddress = LocalApiUrl, - SupportsLibraryMonitor = SupportsLibraryMonitor + LocalAddress = localAddress, + SupportsLibraryMonitor = SupportsLibraryMonitor, + EncoderLocationType = MediaEncoder.EncoderLocationType, + SystemArchitecture = NativeApp.Environment.SystemArchitecture }; } @@ -1123,29 +1145,26 @@ namespace MediaBrowser.Server.Startup.Common get { return !string.IsNullOrWhiteSpace(HttpServer.CertificatePath); } } - public string LocalApiUrl + public async Task<string> GetLocalApiUrl() { - get + try { - try - { - // Return the first matched address, if found, or the first known local address - var address = LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i)); + // Return the first matched address, if found, or the first known local address + var address = (await GetLocalIpAddresses().ConfigureAwait(false)).FirstOrDefault(i => !IPAddress.IsLoopback(i)); - if (address != null) - { - return GetLocalApiUrl(address); - } - - return null; - } - catch (Exception ex) + if (address != null) { - Logger.ErrorException("Error getting local Ip address information", ex); + return GetLocalApiUrl(address); } return null; } + catch (Exception ex) + { + Logger.ErrorException("Error getting local Ip address information", ex); + } + + return null; } public string GetLocalApiUrl(IPAddress ipAddress) @@ -1165,16 +1184,13 @@ namespace MediaBrowser.Server.Startup.Common HttpPort.ToString(CultureInfo.InvariantCulture)); } - public List<IPAddress> LocalIpAddresses + public async Task<List<IPAddress>> GetLocalIpAddresses() { - get - { - var localAddresses = NetworkManager.GetLocalIpAddresses() - .Where(IsIpAddressValid) - .ToList(); + var localAddresses = NetworkManager.GetLocalIpAddresses() + .Where(IsIpAddressValid) + .ToList(); - return localAddresses; - } + return localAddresses; } private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase); @@ -1360,7 +1376,6 @@ namespace MediaBrowser.Server.Startup.Common /// <param name="package">The package that contains the update</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="progress">The progress.</param> - /// <returns>Task.</returns> public override async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress) { await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false); @@ -1404,5 +1419,10 @@ namespace MediaBrowser.Server.Startup.Common return externalDns; } } + + public void LaunchUrl(string url) + { + NativeApp.LaunchUrl(url); + } } } diff --git a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs b/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs index a4504f25a..db48d1110 100644 --- a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs +++ b/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs @@ -15,87 +15,58 @@ namespace MediaBrowser.Server.Startup.Common.Browser /// </summary> /// <param name="page">The page.</param> /// <param name="appHost">The app host.</param> - /// <param name="logger">The logger.</param> - public static void OpenDashboardPage(string page, IServerApplicationHost appHost, ILogger logger) + public static void OpenDashboardPage(string page, IServerApplicationHost appHost) { var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page; - OpenUrl(url, logger); + OpenUrl(appHost, url); } /// <summary> /// Opens the community. /// </summary> - /// <param name="logger">The logger.</param> - public static void OpenCommunity(ILogger logger) + public static void OpenCommunity(IServerApplicationHost appHost) { - OpenUrl("http://emby.media/community", logger); + OpenUrl(appHost, "http://emby.media/community"); } /// <summary> /// Opens the web client. /// </summary> /// <param name="appHost">The app host.</param> - /// <param name="logger">The logger.</param> - public static void OpenWebClient(IServerApplicationHost appHost, ILogger logger) + public static void OpenWebClient(IServerApplicationHost appHost) { - OpenDashboardPage("index.html", appHost, logger); + OpenDashboardPage("index.html", appHost); } /// <summary> /// Opens the dashboard. /// </summary> /// <param name="appHost">The app host.</param> - /// <param name="logger">The logger.</param> - public static void OpenDashboard(IServerApplicationHost appHost, ILogger logger) + public static void OpenDashboard(IServerApplicationHost appHost) { - OpenDashboardPage("dashboard.html", appHost, logger); + OpenDashboardPage("dashboard.html", appHost); } /// <summary> /// Opens the URL. /// </summary> /// <param name="url">The URL.</param> - /// <param name="logger">The logger.</param> - private static void OpenUrl(string url, ILogger logger) + private static void OpenUrl(IServerApplicationHost appHost, string url) { - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = url - }, - - EnableRaisingEvents = true, - }; - - process.Exited += ProcessExited; - try { - process.Start(); + appHost.LaunchUrl(url); + } + catch (NotImplementedException) + { + } catch (Exception ex) { - logger.ErrorException("Error launching url: {0}", ex, url); - - Console.WriteLine("Error launching url: {0}", ex.Message); + Console.WriteLine("Error launching url: " + url); Console.WriteLine(ex.Message); - -//#if !__MonoCS__ -// System.Windows.Forms.MessageBox.Show("There was an error launching your web browser. Please check your default browser settings."); -//#endif } } - - /// <summary> - /// Processes the exited. - /// </summary> - /// <param name="sender">The sender.</param> - /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> - private static void ProcessExited(object sender, EventArgs e) - { - ((Process)sender).Dispose(); - } } } diff --git a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs index 20d4c6b2a..dbfd6f4e8 100644 --- a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs +++ b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs @@ -27,28 +27,27 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints _timer = new PeriodicTimer(obj => { var now = DateTime.UtcNow; - if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15)) + var nativeApp = ((ApplicationHost)_appHost).NativeApp; + + try + { + if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15)) + { + nativeApp.PreventSystemStandby(); + } + else + { + nativeApp.AllowSystemStandby(); + } + } + catch (Exception ex) { - KeepAlive(); + _logger.ErrorException("Error resetting system standby timer", ex); } }, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); } - private void KeepAlive() - { - var nativeApp = ((ApplicationHost)_appHost).NativeApp; - - try - { - nativeApp.PreventSystemStandby(); - } - catch (Exception ex) - { - _logger.ErrorException("Error resetting system standby timer", ex); - } - } - public void Dispose() { if (_timer != null) diff --git a/MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs b/MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs index 854fa44c1..f9d173c59 100644 --- a/MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs +++ b/MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints /// </summary> private void LaunchStartupWizard() { - BrowserLauncher.OpenDashboardPage("wizardstart.html", _appHost, _logger); + BrowserLauncher.OpenDashboardPage("wizardstart.html", _appHost); } /// <summary> diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs deleted file mode 100644 index 60cb50e30..000000000 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs +++ /dev/null @@ -1,142 +0,0 @@ - -namespace MediaBrowser.Server.Startup.Common.FFMpeg -{ - public class FFMpegDownloadInfo - { - public string Version { get; set; } - public string FFMpegFilename { get; set; } - public string FFProbeFilename { get; set; } - public string ArchiveType { get; set; } - public string[] DownloadUrls { get; set; } - - public FFMpegDownloadInfo() - { - DownloadUrls = new string[] { }; - Version = "Path"; - FFMpegFilename = "ffmpeg"; - FFProbeFilename = "ffprobe"; - } - - public static FFMpegDownloadInfo GetInfo(NativeEnvironment environment) - { - var info = new FFMpegDownloadInfo(); - - // Windows builds: http://ffmpeg.zeranoe.com/builds/ - // Linux builds: http://johnvansickle.com/ffmpeg/ - // OS X builds: http://ffmpegmac.net/ - // OS X x64: http://www.evermeet.cx/ffmpeg/ - - switch (environment.OperatingSystem) - { - case OperatingSystem.Bsd: - break; - case OperatingSystem.Linux: - - info.ArchiveType = "7z"; - info.Version = "20160215"; - break; - case OperatingSystem.Osx: - - info.ArchiveType = "7z"; - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - info.Version = "20160124"; - break; - case Architecture.X86: - info.Version = "20150110"; - break; - } - break; - - case OperatingSystem.Windows: - - info.FFMpegFilename = "ffmpeg.exe"; - info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20160131"; - info.ArchiveType = "7z"; - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - break; - case Architecture.X86: - break; - } - break; - } - - info.DownloadUrls = GetDownloadUrls(environment); - - return info; - } - - private static string[] GetDownloadUrls(NativeEnvironment environment) - { - switch (environment.OperatingSystem) - { - case OperatingSystem.Windows: - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160131-win64.7z", - "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20151109-git-480bad7-win64-static.7z" - }; - case Architecture.X86: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160131-win32.7z", - "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20151109-git-480bad7-win32-static.7z" - }; - } - break; - - case OperatingSystem.Osx: - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z" - }; - case Architecture.X86: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x86-2.5.3.7z" - }; - } - break; - - case OperatingSystem.Linux: - - switch (environment.SystemArchitecture) - { - case Architecture.X86_X64: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z" - }; - case Architecture.X86: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z" - }; - case Architecture.Arm: - return new[] - { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-arm.7z" - }; - } - break; - } - - // No version available - return new string[] { }; - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs deleted file mode 100644 index 000568c15..000000000 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs +++ /dev/null @@ -1,439 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using Mono.Unix.Native; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using CommonIO; - -namespace MediaBrowser.Server.Startup.Common.FFMpeg -{ - public class FFMpegDownloader - { - private readonly IHttpClient _httpClient; - private readonly IApplicationPaths _appPaths; - private readonly ILogger _logger; - private readonly IZipClient _zipClient; - private readonly IFileSystem _fileSystem; - private readonly NativeEnvironment _environment; - - private readonly string[] _fontUrls = - { - "https://github.com/MediaBrowser/MediaBrowser.Resources/raw/master/ffmpeg/ARIALUNI.7z" - }; - - public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment) - { - _logger = logger; - _appPaths = appPaths; - _httpClient = httpClient; - _zipClient = zipClient; - _fileSystem = fileSystem; - _environment = environment; - } - - public async Task<FFMpegInfo> GetFFMpegInfo(NativeEnvironment environment, StartupOptions options, IProgress<double> progress) - { - var customffMpegPath = options.GetOption("-ffmpeg"); - var customffProbePath = options.GetOption("-ffprobe"); - - if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath)) - { - return new FFMpegInfo - { - ProbePath = customffProbePath, - EncoderPath = customffMpegPath, - Version = "custom" - }; - } - - var downloadInfo = FFMpegDownloadInfo.GetInfo(environment); - - var version = downloadInfo.Version; - - if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase)) - { - return new FFMpegInfo - { - ProbePath = downloadInfo.FFProbeFilename, - EncoderPath = downloadInfo.FFMpegFilename, - Version = version - }; - } - - var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); - var versionedDirectoryPath = Path.Combine(rootEncoderPath, version); - - var info = new FFMpegInfo - { - ProbePath = Path.Combine(versionedDirectoryPath, downloadInfo.FFProbeFilename), - EncoderPath = Path.Combine(versionedDirectoryPath, downloadInfo.FFMpegFilename), - Version = version - }; - - _fileSystem.CreateDirectory(versionedDirectoryPath); - - var excludeFromDeletions = new List<string> { versionedDirectoryPath }; - - if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(info.EncoderPath)) - { - // ffmpeg not present. See if there's an older version we can start with - var existingVersion = GetExistingVersion(info, rootEncoderPath); - - // No older version. Need to download and block until complete - if (existingVersion == null) - { - await DownloadFFMpeg(downloadInfo, versionedDirectoryPath, progress).ConfigureAwait(false); - } - else - { - // Older version found. - // Start with that. Download new version in the background. - var newPath = versionedDirectoryPath; - Task.Run(() => DownloadFFMpegInBackground(downloadInfo, newPath)); - - info = existingVersion; - versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath); - - excludeFromDeletions.Add(versionedDirectoryPath); - } - } - - await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false); - - DeleteOlderFolders(Path.GetDirectoryName(versionedDirectoryPath), excludeFromDeletions); - - // Allow just one of these to be overridden, if desired. - if (!string.IsNullOrWhiteSpace(customffMpegPath)) - { - info.EncoderPath = customffMpegPath; - } - if (!string.IsNullOrWhiteSpace(customffProbePath)) - { - info.EncoderPath = customffProbePath; - } - - return info; - } - - private void DeleteOlderFolders(string path, IEnumerable<string> excludeFolders) - { - var folders = Directory.GetDirectories(path) - .Where(i => !excludeFolders.Contains(i, StringComparer.OrdinalIgnoreCase)) - .ToList(); - - foreach (var folder in folders) - { - DeleteFolder(folder); - } - } - - private void DeleteFolder(string path) - { - try - { - _fileSystem.DeleteDirectory(path, true); - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting {0}", ex, path); - } - } - - private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath) - { - var encoderFilename = Path.GetFileName(info.EncoderPath); - var probeFilename = Path.GetFileName(info.ProbePath); - - foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly) - .ToList()) - { - var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).ToList(); - - var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase)); - var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase)); - - if (!string.IsNullOrWhiteSpace(encoder) && - !string.IsNullOrWhiteSpace(probe)) - { - return new FFMpegInfo - { - EncoderPath = encoder, - ProbePath = probe, - Version = Path.GetFileName(Path.GetDirectoryName(probe)) - }; - } - } - - return null; - } - - private async void DownloadFFMpegInBackground(FFMpegDownloadInfo downloadinfo, string directory) - { - try - { - await DownloadFFMpeg(downloadinfo, directory, new Progress<double>()).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading ffmpeg", ex); - } - } - - private async Task DownloadFFMpeg(FFMpegDownloadInfo downloadinfo, string directory, IProgress<double> progress) - { - foreach (var url in downloadinfo.DownloadUrls) - { - progress.Report(0); - - try - { - var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions - { - Url = url, - CancellationToken = CancellationToken.None, - Progress = progress - - }).ConfigureAwait(false); - - ExtractFFMpeg(downloadinfo, tempFile, directory); - return; - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading {0}", ex, url); - } - } - - if (downloadinfo.DownloadUrls.Length == 0) - { - throw new ApplicationException("ffmpeg unvailable. Please install it and start the server with two command line arguments: -ffmpeg \"{PATH}\" and -ffprobe \"{PATH}\""); - } - else - { - throw new ApplicationException("Unable to download required components. Please try again later."); - } - } - - private void ExtractFFMpeg(FFMpegDownloadInfo downloadinfo, string tempFile, string targetFolder) - { - _logger.Info("Extracting ffmpeg from {0}", tempFile); - - var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); - - _fileSystem.CreateDirectory(tempFolder); - - try - { - ExtractArchive(downloadinfo, tempFile, tempFolder); - - var files = Directory.EnumerateFiles(tempFolder, "*", SearchOption.AllDirectories) - .ToList(); - - foreach (var file in files.Where(i => - { - var filename = Path.GetFileName(i); - - return - string.Equals(filename, downloadinfo.FFProbeFilename, StringComparison.OrdinalIgnoreCase) || - string.Equals(filename, downloadinfo.FFMpegFilename, StringComparison.OrdinalIgnoreCase); - })) - { - var targetFile = Path.Combine(targetFolder, Path.GetFileName(file)); - _fileSystem.CopyFile(file, targetFile, true); - SetFilePermissions(targetFile); - } - } - finally - { - DeleteFile(tempFile); - } - } - - private void SetFilePermissions(string path) - { - // Linux: File permission to 666, and user's execute bit - if (_environment.OperatingSystem == OperatingSystem.Bsd || _environment.OperatingSystem == OperatingSystem.Linux || _environment.OperatingSystem == OperatingSystem.Osx) - { - _logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path); - - Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH); - } - } - - private void ExtractArchive(FFMpegDownloadInfo downloadinfo, string archivePath, string targetPath) - { - _logger.Info("Extracting {0} to {1}", archivePath, targetPath); - - if (string.Equals(downloadinfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase)) - { - _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); - } - else if (string.Equals(downloadinfo.ArchiveType, "gz", StringComparison.OrdinalIgnoreCase)) - { - _zipClient.ExtractAllFromTar(archivePath, targetPath, true); - } - } - private void Extract7zArchive(string archivePath, string targetPath) - { - _logger.Info("Extracting {0} to {1}", archivePath, targetPath); - - _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); - } - - private void DeleteFile(string path) - { - try - { - _fileSystem.DeleteFile(path); - } - catch (IOException ex) - { - _logger.ErrorException("Error deleting temp file {0}", ex, path); - } - } - - /// <summary> - /// Extracts the fonts. - /// </summary> - /// <param name="targetPath">The target path.</param> - /// <returns>Task.</returns> - private async Task DownloadFonts(string targetPath) - { - try - { - var fontsDirectory = Path.Combine(targetPath, "fonts"); - - _fileSystem.CreateDirectory(fontsDirectory); - - const string fontFilename = "ARIALUNI.TTF"; - - var fontFile = Path.Combine(fontsDirectory, fontFilename); - - if (_fileSystem.FileExists(fontFile)) - { - await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); - } - else - { - // Kick this off, but no need to wait on it - Task.Run(async () => - { - await DownloadFontFile(fontsDirectory, fontFilename, new Progress<double>()).ConfigureAwait(false); - - await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); - }); - } - } - catch (HttpException ex) - { - // Don't let the server crash because of this - _logger.ErrorException("Error downloading ffmpeg font files", ex); - } - catch (Exception ex) - { - // Don't let the server crash because of this - _logger.ErrorException("Error writing ffmpeg font files", ex); - } - } - - /// <summary> - /// Downloads the font file. - /// </summary> - /// <param name="fontsDirectory">The fonts directory.</param> - /// <param name="fontFilename">The font filename.</param> - /// <returns>Task.</returns> - private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress) - { - var existingFile = Directory - .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories) - .FirstOrDefault(); - - if (existingFile != null) - { - try - { - _fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true); - return; - } - catch (IOException ex) - { - // Log this, but don't let it fail the operation - _logger.ErrorException("Error copying file", ex); - } - } - - string tempFile = null; - - foreach (var url in _fontUrls) - { - progress.Report(0); - - try - { - tempFile = await _httpClient.GetTempFile(new HttpRequestOptions - { - Url = url, - Progress = progress - - }).ConfigureAwait(false); - - break; - } - catch (Exception ex) - { - // The core can function without the font file, so handle this - _logger.ErrorException("Failed to download ffmpeg font file from {0}", ex, url); - } - } - - if (string.IsNullOrEmpty(tempFile)) - { - return; - } - - Extract7zArchive(tempFile, fontsDirectory); - - try - { - _fileSystem.DeleteFile(tempFile); - } - catch (IOException ex) - { - // Log this, but don't let it fail the operation - _logger.ErrorException("Error deleting temp file {0}", ex, tempFile); - } - } - - /// <summary> - /// Writes the font config file. - /// </summary> - /// <param name="fontsDirectory">The fonts directory.</param> - /// <returns>Task.</returns> - private async Task WriteFontConfigFile(string fontsDirectory) - { - const string fontConfigFilename = "fonts.conf"; - var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename); - - if (!_fileSystem.FileExists(fontConfigFile)) - { - var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory); - - var bytes = Encoding.UTF8.GetBytes(contents); - - using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write, - FileShare.Read, true)) - { - await fileStream.WriteAsync(bytes, 0, bytes.Length); - } - } - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs new file mode 100644 index 000000000..a2a44f805 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs @@ -0,0 +1,20 @@ + +namespace MediaBrowser.Server.Startup.Common.FFMpeg +{ + public class FFMpegInstallInfo + { + public string Version { get; set; } + public string FFMpegFilename { get; set; } + public string FFProbeFilename { get; set; } + public string ArchiveType { get; set; } + public string[] DownloadUrls { get; set; } + + public FFMpegInstallInfo() + { + DownloadUrls = new string[] { }; + Version = "Path"; + FFMpegFilename = "ffmpeg"; + FFProbeFilename = "ffprobe"; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs new file mode 100644 index 000000000..68e2a4927 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs @@ -0,0 +1,248 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using Mono.Unix.Native; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommonIO; + +namespace MediaBrowser.Server.Startup.Common.FFMpeg +{ + public class FFMpegLoader + { + private readonly IHttpClient _httpClient; + private readonly IApplicationPaths _appPaths; + private readonly ILogger _logger; + private readonly IZipClient _zipClient; + private readonly IFileSystem _fileSystem; + private readonly NativeEnvironment _environment; + private readonly FFMpegInstallInfo _ffmpegInstallInfo; + + public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment, FFMpegInstallInfo ffmpegInstallInfo) + { + _logger = logger; + _appPaths = appPaths; + _httpClient = httpClient; + _zipClient = zipClient; + _fileSystem = fileSystem; + _environment = environment; + _ffmpegInstallInfo = ffmpegInstallInfo; + } + + public async Task<FFMpegInfo> GetFFMpegInfo(NativeEnvironment environment, StartupOptions options, IProgress<double> progress) + { + var customffMpegPath = options.GetOption("-ffmpeg"); + var customffProbePath = options.GetOption("-ffprobe"); + + if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath)) + { + return new FFMpegInfo + { + ProbePath = customffProbePath, + EncoderPath = customffMpegPath, + Version = "external" + }; + } + + var downloadInfo = _ffmpegInstallInfo; + + var version = downloadInfo.Version; + + if (string.Equals(version, "path", StringComparison.OrdinalIgnoreCase)) + { + return new FFMpegInfo + { + ProbePath = downloadInfo.FFProbeFilename, + EncoderPath = downloadInfo.FFMpegFilename, + Version = version + }; + } + + if (string.Equals(version, "0", StringComparison.OrdinalIgnoreCase)) + { + return new FFMpegInfo(); + } + + var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); + var versionedDirectoryPath = Path.Combine(rootEncoderPath, version); + + var info = new FFMpegInfo + { + ProbePath = Path.Combine(versionedDirectoryPath, downloadInfo.FFProbeFilename), + EncoderPath = Path.Combine(versionedDirectoryPath, downloadInfo.FFMpegFilename), + Version = version + }; + + _fileSystem.CreateDirectory(versionedDirectoryPath); + + var excludeFromDeletions = new List<string> { versionedDirectoryPath }; + + if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(info.EncoderPath)) + { + // ffmpeg not present. See if there's an older version we can start with + var existingVersion = GetExistingVersion(info, rootEncoderPath); + + // No older version. Need to download and block until complete + if (existingVersion == null) + { + var success = await DownloadFFMpeg(downloadInfo, versionedDirectoryPath, progress).ConfigureAwait(false); + if (!success) + { + return new FFMpegInfo(); + } + } + else + { + info = existingVersion; + versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath); + excludeFromDeletions.Add(versionedDirectoryPath); + } + } + + // Allow just one of these to be overridden, if desired. + if (!string.IsNullOrWhiteSpace(customffMpegPath)) + { + info.EncoderPath = customffMpegPath; + } + if (!string.IsNullOrWhiteSpace(customffProbePath)) + { + info.EncoderPath = customffProbePath; + } + + return info; + } + + private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath) + { + var encoderFilename = Path.GetFileName(info.EncoderPath); + var probeFilename = Path.GetFileName(info.ProbePath); + + foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly) + .ToList()) + { + var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).ToList(); + + var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase)); + var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrWhiteSpace(encoder) && + !string.IsNullOrWhiteSpace(probe)) + { + return new FFMpegInfo + { + EncoderPath = encoder, + ProbePath = probe, + Version = Path.GetFileName(Path.GetDirectoryName(probe)) + }; + } + } + + return null; + } + + private async Task<bool> DownloadFFMpeg(FFMpegInstallInfo downloadinfo, string directory, IProgress<double> progress) + { + foreach (var url in downloadinfo.DownloadUrls) + { + progress.Report(0); + + try + { + var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions + { + Url = url, + CancellationToken = CancellationToken.None, + Progress = progress + + }).ConfigureAwait(false); + + ExtractFFMpeg(downloadinfo, tempFile, directory); + return true; + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading {0}", ex, url); + } + } + return false; + } + + private void ExtractFFMpeg(FFMpegInstallInfo downloadinfo, string tempFile, string targetFolder) + { + _logger.Info("Extracting ffmpeg from {0}", tempFile); + + var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); + + _fileSystem.CreateDirectory(tempFolder); + + try + { + ExtractArchive(downloadinfo, tempFile, tempFolder); + + var files = Directory.EnumerateFiles(tempFolder, "*", SearchOption.AllDirectories) + .ToList(); + + foreach (var file in files.Where(i => + { + var filename = Path.GetFileName(i); + + return + string.Equals(filename, downloadinfo.FFProbeFilename, StringComparison.OrdinalIgnoreCase) || + string.Equals(filename, downloadinfo.FFMpegFilename, StringComparison.OrdinalIgnoreCase); + })) + { + var targetFile = Path.Combine(targetFolder, Path.GetFileName(file)); + _fileSystem.CopyFile(file, targetFile, true); + SetFilePermissions(targetFile); + } + } + finally + { + DeleteFile(tempFile); + } + } + + private void SetFilePermissions(string path) + { + // Linux: File permission to 666, and user's execute bit + if (_environment.OperatingSystem == OperatingSystem.Bsd || _environment.OperatingSystem == OperatingSystem.Linux || _environment.OperatingSystem == OperatingSystem.Osx) + { + _logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path); + + Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH); + } + } + + private void ExtractArchive(FFMpegInstallInfo downloadinfo, string archivePath, string targetPath) + { + _logger.Info("Extracting {0} to {1}", archivePath, targetPath); + + if (string.Equals(downloadinfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase)) + { + _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); + } + else if (string.Equals(downloadinfo.ArchiveType, "gz", StringComparison.OrdinalIgnoreCase)) + { + _zipClient.ExtractAllFromTar(archivePath, targetPath, true); + } + } + + private void DeleteFile(string path) + { + try + { + _fileSystem.DeleteFile(path); + } + catch (IOException ex) + { + _logger.ErrorException("Error deleting temp file {0}", ex, path); + } + } + + } +} diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs deleted file mode 100644 index 5ba5fb44a..000000000 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs +++ /dev/null @@ -1,162 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Logging; -using System; -using System.Diagnostics; -using System.IO; -using System.Collections.Generic; -using CommonIO; - -namespace MediaBrowser.Server.Startup.Common.FFMpeg -{ - public class FFmpegValidator - { - private readonly ILogger _logger; - private readonly IApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - - public FFmpegValidator(ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem) - { - _logger = logger; - _appPaths = appPaths; - _fileSystem = fileSystem; - } - - public Tuple<List<string>,List<string>> Validate(FFMpegInfo info) - { - _logger.Info("FFMpeg: {0}", info.EncoderPath); - _logger.Info("FFProbe: {0}", info.ProbePath); - - var decoders = GetDecoders(info.EncoderPath); - var encoders = GetEncoders(info.EncoderPath); - - return new Tuple<List<string>, List<string>>(decoders, encoders); - } - - private List<string> GetDecoders(string ffmpegPath) - { - string output = string.Empty; - try - { - output = GetFFMpegOutput(ffmpegPath, "-decoders"); - } - catch - { - } - - var found = new List<string>(); - var required = new[] - { - "h264_qsv", - "mpeg2_qsv", - "vc1_qsv" - }; - - foreach (var codec in required) - { - var srch = " " + codec + " "; - - if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) - { - _logger.Warn("ffmpeg is missing decoder " + codec); - } - else - { - found.Add(codec); - } - } - - return found; - } - - private List<string> GetEncoders(string ffmpegPath) - { - string output = null; - try - { - output = GetFFMpegOutput(ffmpegPath, "-encoders"); - } - catch - { - } - - var found = new List<string>(); - var required = new[] - { - "libx264", - "libx265", - "mpeg4", - "msmpeg4", - //"libvpx", - //"libvpx-vp9", - "aac", - "ac3", - "libmp3lame", - //"libvorbis", - "srt" - }; - - foreach (var codec in required) - { - var srch = " " + codec + " "; - - if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) - { - _logger.Warn("ffmpeg is missing encoder " + codec); - } - else - { - found.Add(codec); - } - } - - return found; - } - - private string GetFFMpegOutput(string path, string arguments) - { - var process = new Process - { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = path, - Arguments = arguments, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false, - RedirectStandardOutput = true, - RedirectStandardError = true - } - }; - - using (process) - { - process.Start(); - - try - { - process.BeginErrorReadLine(); - - using (var reader = new StreamReader(process.StandardOutput.BaseStream)) - { - return reader.ReadToEnd(); - } - } - catch - { - // Hate having to do this - try - { - process.Kill(); - } - catch (Exception ex1) - { - _logger.ErrorException("Error killing ffmpeg", ex1); - } - - throw; - } - } - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs index 9df670bda..c13d3624e 100644 --- a/MediaBrowser.Server.Startup.Common/INativeApp.cs +++ b/MediaBrowser.Server.Startup.Common/INativeApp.cs @@ -3,6 +3,8 @@ using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Reflection; using MediaBrowser.Controller.Power; +using MediaBrowser.Server.Implementations.Persistence; +using MediaBrowser.Server.Startup.Common.FFMpeg; namespace MediaBrowser.Server.Startup.Common { @@ -92,10 +94,18 @@ namespace MediaBrowser.Server.Startup.Common /// </summary> void PreventSystemStandby(); + void AllowSystemStandby(); + /// <summary> /// Gets the power management. /// </summary> /// <returns>IPowerManagement.</returns> IPowerManagement GetPowerManagement(); + + FFMpegInstallInfo GetFfmpegInstallInfo(); + + void LaunchUrl(string url); + + IDbConnector GetDbConnector(); } } diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index 80ce88fa3..808d25fc9 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -65,17 +65,18 @@ <Compile Include="Browser\BrowserLauncher.cs" /> <Compile Include="EntryPoints\KeepServerAwake.cs" /> <Compile Include="EntryPoints\StartupWizard.cs" /> - <Compile Include="FFMpeg\FFMpegDownloader.cs" /> - <Compile Include="FFMpeg\FFMpegDownloadInfo.cs" /> + <Compile Include="FFMpeg\FFMpegLoader.cs" /> + <Compile Include="FFMpeg\FFMpegInstallInfo.cs" /> <Compile Include="FFMpeg\FFMpegInfo.cs" /> - <Compile Include="FFMpeg\FFmpegValidator.cs" /> <Compile Include="INativeApp.cs" /> <Compile Include="MbLinkShortcutHandler.cs" /> + <Compile Include="Migrations\CollectionGroupingMigration.cs" /> + <Compile Include="Migrations\CollectionsViewMigration.cs" /> + <Compile Include="Migrations\FolderViewSettingMigration.cs" /> <Compile Include="Migrations\IVersionMigration.cs" /> <Compile Include="Migrations\DbMigration.cs" /> <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" /> <Compile Include="Migrations\OmdbEpisodeProviderMigration.cs" /> - <Compile Include="Migrations\RenameXmlOptions.cs" /> <Compile Include="NativeEnvironment.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="StartupOptions.cs" /> diff --git a/MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs new file mode 100644 index 000000000..b497eeb42 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Migrations/CollectionGroupingMigration.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Server.Startup.Common.Migrations +{ + public class CollectionGroupingMigration : IVersionMigration + { + private readonly IServerConfigurationManager _config; + private readonly IUserManager _userManager; + + public CollectionGroupingMigration(IServerConfigurationManager config, IUserManager userManager) + { + _config = config; + _userManager = userManager; + } + + public void Run() + { + var migrationKey = this.GetType().Name; + var migrationKeyList = _config.Configuration.Migrations.ToList(); + + if (!migrationKeyList.Contains(migrationKey)) + { + if (_config.Configuration.IsStartupWizardCompleted) + { + if (_userManager.Users.Any(i => i.Configuration.GroupMoviesIntoBoxSets)) + { + _config.Configuration.EnableGroupingIntoCollections = true; + } + } + + migrationKeyList.Add(migrationKey); + _config.Configuration.Migrations = migrationKeyList.ToArray(); + _config.SaveConfiguration(); + } + + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/CollectionsViewMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/CollectionsViewMigration.cs new file mode 100644 index 000000000..c6186ce08 --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Migrations/CollectionsViewMigration.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Server.Startup.Common.Migrations +{ + public class CollectionsViewMigration : IVersionMigration + { + private readonly IServerConfigurationManager _config; + private readonly IUserManager _userManager; + + public CollectionsViewMigration(IServerConfigurationManager config, IUserManager userManager) + { + _config = config; + _userManager = userManager; + } + + public void Run() + { + var migrationKey = this.GetType().Name; + var migrationKeyList = _config.Configuration.Migrations.ToList(); + + if (!migrationKeyList.Contains(migrationKey)) + { + if (_config.Configuration.IsStartupWizardCompleted) + { + if (_userManager.Users.Any(i => i.Configuration.DisplayCollectionsView)) + { + _config.Configuration.DisplayCollectionsView = true; + } + } + + migrationKeyList.Add(migrationKey); + _config.Configuration.Migrations = migrationKeyList.ToArray(); + _config.SaveConfiguration(); + } + + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs index 65517c09c..f0cb9e84e 100644 --- a/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs +++ b/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs @@ -18,6 +18,7 @@ namespace MediaBrowser.Server.Startup.Common.Migrations public void Run() { + // If a forced migration is required, do that now if (_config.Configuration.MigrationVersion < CleanDatabaseScheduledTask.MigrationVersion) { if (!_config.Configuration.IsStartupWizardCompleted) @@ -36,6 +37,25 @@ namespace MediaBrowser.Server.Startup.Common.Migrations _taskManager.Execute<CleanDatabaseScheduledTask>(); }); + + return; + } + + if (_config.Configuration.SchemaVersion < SqliteItemRepository.LatestSchemaVersion) + { + if (!_config.Configuration.IsStartupWizardCompleted) + { + _config.Configuration.SchemaVersion = SqliteItemRepository.LatestSchemaVersion; + _config.SaveConfiguration(); + return; + } + + Task.Run(async () => + { + await Task.Delay(1000).ConfigureAwait(false); + + _taskManager.Execute<CleanDatabaseScheduledTask>(); + }); } } } diff --git a/MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs new file mode 100644 index 000000000..12054864b --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs @@ -0,0 +1,40 @@ +using System.Linq; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Server.Startup.Common.Migrations +{ + public class FolderViewSettingMigration : IVersionMigration + { + private readonly IServerConfigurationManager _config; + private readonly IUserManager _userManager; + + public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager) + { + _config = config; + _userManager = userManager; + } + + public void Run() + { + var migrationKey = this.GetType().Name; + var migrationKeyList = _config.Configuration.Migrations.ToList(); + + if (!migrationKeyList.Contains(migrationKey)) + { + if (_config.Configuration.IsStartupWizardCompleted) + { + if (_userManager.Users.Any(i => i.Configuration.DisplayFoldersView)) + { + _config.Configuration.EnableFolderView = true; + } + } + + migrationKeyList.Add(migrationKey); + _config.Configuration.Migrations = migrationKeyList.ToArray(); + _config.SaveConfiguration(); + } + + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/RenameXmlOptions.cs b/MediaBrowser.Server.Startup.Common/Migrations/RenameXmlOptions.cs deleted file mode 100644 index 49114b96f..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/RenameXmlOptions.cs +++ /dev/null @@ -1,61 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using System; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - public class RenameXmlOptions : IVersionMigration - { - private readonly IServerConfigurationManager _config; - - public RenameXmlOptions(IServerConfigurationManager config) - { - _config = config; - } - - public void Run() - { - var changed = false; - - foreach (var option in _config.Configuration.MetadataOptions) - { - if (Migrate(option.DisabledMetadataSavers)) - { - changed = true; - } - if (Migrate(option.LocalMetadataReaderOrder)) - { - changed = true; - } - } - - if (changed) - { - _config.SaveConfiguration(); - } - } - - private bool Migrate(string[] options) - { - var changed = false; - - if (options != null) - { - for (var i = 0; i < options.Length; i++) - { - if (string.Equals(options[i], "Media Browser Legacy Xml", StringComparison.OrdinalIgnoreCase)) - { - options[i] = "Emby Xml"; - changed = true; - } - else if (string.Equals(options[i], "Media Browser Xml", StringComparison.OrdinalIgnoreCase)) - { - options[i] = "Emby Xml"; - changed = true; - } - } - } - - return changed; - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs b/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs index 5b45afe73..b30509982 100644 --- a/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs +++ b/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs @@ -1,4 +1,5 @@ - +using MediaBrowser.Model.System; + namespace MediaBrowser.Server.Startup.Common { public class NativeEnvironment @@ -15,11 +16,4 @@ namespace MediaBrowser.Server.Startup.Common Bsd = 2, Linux = 3 } - - public enum Architecture - { - X86 = 0, - X86_X64 = 1, - Arm = 2 - } } |
