mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-15 09:59:06 -07:00
support run as service
This commit is contained in:
parent
b5615cb233
commit
2e511fba83
@ -1,6 +1,5 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Implementations.Logging;
|
||||
using MediaBrowser.Common.Implementations.NetworkManagement;
|
||||
using MediaBrowser.Common.Implementations.ScheduledTasks;
|
||||
using MediaBrowser.Common.Implementations.Security;
|
||||
@ -71,7 +70,7 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// Gets the application paths.
|
||||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
protected TApplicationPathsType ApplicationPaths = new TApplicationPathsType();
|
||||
protected TApplicationPathsType ApplicationPaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The container
|
||||
@ -153,11 +152,12 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
|
||||
/// </summary>
|
||||
protected BaseApplicationHost()
|
||||
protected BaseApplicationHost(TApplicationPathsType applicationPaths, ILogManager logManager)
|
||||
{
|
||||
FailedAssemblies = new List<string>();
|
||||
|
||||
LogManager = new NlogManager(ApplicationPaths.LogDirectoryPath, LogFilePrefixName);
|
||||
ApplicationPaths = applicationPaths;
|
||||
LogManager = logManager;
|
||||
|
||||
ConfigurationManager = GetConfigurationManager();
|
||||
}
|
||||
@ -172,7 +172,10 @@ namespace MediaBrowser.Common.Implementations
|
||||
|
||||
Logger = LogManager.GetLogger("App");
|
||||
|
||||
LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info);
|
||||
LogManager.LogSeverity = ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
|
||||
? LogSeverity.Debug
|
||||
: LogSeverity.Info;
|
||||
|
||||
OnLoggerLoaded();
|
||||
|
||||
DiscoverTypes();
|
||||
@ -211,12 +214,6 @@ namespace MediaBrowser.Common.Implementations
|
||||
/// <returns>IEnumerable{Assembly}.</returns>
|
||||
protected abstract IEnumerable<Assembly> GetComposablePartAssemblies();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the log file prefix.
|
||||
/// </summary>
|
||||
/// <value>The name of the log file prefix.</value>
|
||||
protected abstract string LogFilePrefixName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration manager.
|
||||
/// </summary>
|
||||
|
@ -26,6 +26,15 @@ namespace MediaBrowser.Common.Implementations
|
||||
_useDebugPath = useDebugPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
|
||||
/// </summary>
|
||||
/// <param name="programDataPath">The program data path.</param>
|
||||
protected BaseApplicationPaths(string programDataPath)
|
||||
{
|
||||
_programDataPath = programDataPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _program data path
|
||||
/// </summary>
|
||||
|
@ -1,10 +1,10 @@
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Logging
|
||||
@ -45,6 +45,41 @@ namespace MediaBrowser.Common.Implementations.Logging
|
||||
LogFilePrefix = logFileNamePrefix;
|
||||
}
|
||||
|
||||
private LogSeverity _severity = LogSeverity.Debug;
|
||||
public LogSeverity LogSeverity
|
||||
{
|
||||
get
|
||||
{
|
||||
return _severity;
|
||||
}
|
||||
set
|
||||
{
|
||||
var changed = _severity != value;
|
||||
|
||||
_severity = value;
|
||||
|
||||
if (changed)
|
||||
{
|
||||
UpdateLogLevel(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLogLevel(LogSeverity newLevel)
|
||||
{
|
||||
var level = GetLogLevel(newLevel);
|
||||
|
||||
var rules = LogManager.Configuration.LoggingRules;
|
||||
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
if (!rule.IsLoggingEnabledForLevel(level))
|
||||
{
|
||||
rule.EnableLoggingForLevel(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the file target.
|
||||
/// </summary>
|
||||
@ -154,6 +189,8 @@ namespace MediaBrowser.Common.Implementations.Logging
|
||||
|
||||
AddFileTarget(LogFilePath, level);
|
||||
|
||||
LogSeverity = level;
|
||||
|
||||
if (LoggerLoaded != null)
|
||||
{
|
||||
Task.Run(() =>
|
||||
|
@ -25,11 +25,5 @@ namespace MediaBrowser.Controller
|
||||
/// </summary>
|
||||
/// <value>The HTTP server URL prefix.</value>
|
||||
string HttpServerUrlPrefix { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is background service.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
|
||||
bool IsBackgroundService { get; }
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,12 @@ namespace MediaBrowser.Model.Logging
|
||||
/// </summary>
|
||||
public interface ILogManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the log level.
|
||||
/// </summary>
|
||||
/// <value>The log level.</value>
|
||||
LogSeverity LogSeverity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the logger.
|
||||
/// </summary>
|
||||
|
@ -80,12 +80,6 @@ namespace MediaBrowser.Model.System
|
||||
/// <value>The HTTP server port number.</value>
|
||||
public int HttpServerPortNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is background service.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
|
||||
public bool IsBackgroundService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemInfo" /> class.
|
||||
/// </summary>
|
||||
|
@ -26,6 +26,17 @@ namespace MediaBrowser.Server.Implementations
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class.
|
||||
/// </summary>
|
||||
/// <param name="programDataPath">The program data path.</param>
|
||||
public ServerApplicationPaths(string programDataPath)
|
||||
: base(programDataPath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the base root media directory
|
||||
/// </summary>
|
||||
@ -117,7 +128,7 @@ namespace MediaBrowser.Server.Implementations
|
||||
return Path.Combine(ItemsByNamePath, "MusicGenre");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the Studio directory
|
||||
/// </summary>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
@ -12,32 +13,35 @@ namespace MediaBrowser.ServerApplication
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application, IApplicationInterface
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the logger.
|
||||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
protected ILogger Logger { get; set; }
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the composition root.
|
||||
/// </summary>
|
||||
/// <value>The composition root.</value>
|
||||
protected ApplicationHost CompositionRoot { get; set; }
|
||||
private readonly ApplicationHost _appHost;
|
||||
|
||||
public event EventHandler AppStarted;
|
||||
|
||||
public bool IsRunningAsService { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="App" /> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public App()
|
||||
public App(ApplicationHost appHost, ILogger logger, bool isRunningAsService)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
_appHost = appHost;
|
||||
_logger = logger;
|
||||
IsRunningAsService = isRunningAsService;
|
||||
|
||||
public bool IsBackgroundService
|
||||
{
|
||||
get { return false; }
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -51,7 +55,7 @@ namespace MediaBrowser.ServerApplication
|
||||
|
||||
public void OnUnhandledException(Exception ex)
|
||||
{
|
||||
Logger.ErrorException("UnhandledException", ex);
|
||||
_logger.ErrorException("UnhandledException", ex);
|
||||
|
||||
MessageBox.Show("Unhandled exception: " + ex.Message);
|
||||
}
|
||||
@ -70,27 +74,32 @@ namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
try
|
||||
{
|
||||
CompositionRoot = new ApplicationHost(this);
|
||||
if (!IsRunningAsService)
|
||||
{
|
||||
ShowSplashWindow();
|
||||
}
|
||||
|
||||
Logger = CompositionRoot.LogManager.GetLogger("App");
|
||||
await _appHost.Init();
|
||||
|
||||
var splash = new SplashWindow(CompositionRoot.ApplicationVersion);
|
||||
if (!IsRunningAsService)
|
||||
{
|
||||
HideSplashWindow();
|
||||
}
|
||||
|
||||
splash.Show();
|
||||
var task = _appHost.RunStartupTasks();
|
||||
|
||||
await CompositionRoot.Init();
|
||||
if (!IsRunningAsService)
|
||||
{
|
||||
ShowMainWindow();
|
||||
}
|
||||
|
||||
splash.Hide();
|
||||
|
||||
var task = CompositionRoot.RunStartupTasks();
|
||||
|
||||
new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager, CompositionRoot.UserManager, CompositionRoot.LibraryManager, CompositionRoot.JsonSerializer, CompositionRoot.DisplayPreferencesRepository).Show();
|
||||
EventHelper.FireEventIfNotNull(AppStarted, this, EventArgs.Empty, _logger);
|
||||
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error launching application", ex);
|
||||
_logger.ErrorException("Error launching application", ex);
|
||||
|
||||
MessageBox.Show("There was an error launching Media Browser: " + ex.Message);
|
||||
|
||||
@ -99,27 +108,53 @@ namespace MediaBrowser.ServerApplication
|
||||
}
|
||||
}
|
||||
|
||||
private MainWindow _mainWindow;
|
||||
private void ShowMainWindow()
|
||||
{
|
||||
var host = _appHost;
|
||||
|
||||
var win = new MainWindow(host.LogManager, host,
|
||||
host.ServerConfigurationManager, host.UserManager,
|
||||
host.LibraryManager, host.JsonSerializer,
|
||||
host.DisplayPreferencesRepository);
|
||||
|
||||
win.Show();
|
||||
|
||||
_mainWindow = win;
|
||||
}
|
||||
|
||||
private void HideMainWindow()
|
||||
{
|
||||
if (_mainWindow != null)
|
||||
{
|
||||
_mainWindow.Hide();
|
||||
_mainWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
private SplashWindow _splashWindow;
|
||||
private void ShowSplashWindow()
|
||||
{
|
||||
var win = new SplashWindow(_appHost.ApplicationVersion);
|
||||
win.Show();
|
||||
|
||||
_splashWindow = win;
|
||||
}
|
||||
|
||||
private void HideSplashWindow()
|
||||
{
|
||||
if (_splashWindow != null)
|
||||
{
|
||||
_splashWindow.Hide();
|
||||
_splashWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ShutdownApplication()
|
||||
{
|
||||
Dispatcher.Invoke(Shutdown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
|
||||
/// </summary>
|
||||
/// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the event data.</param>
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
MainStartup.ReleaseMutex();
|
||||
|
||||
base.OnExit(e);
|
||||
|
||||
if (CompositionRoot != null)
|
||||
{
|
||||
CompositionRoot.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the dashboard page.
|
||||
/// </summary>
|
||||
@ -172,20 +207,5 @@ namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
((Process)sender).Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restarts this instance.
|
||||
/// </summary>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
public void RestartApplication()
|
||||
{
|
||||
Dispatcher.Invoke(MainStartup.ReleaseMutex);
|
||||
|
||||
CompositionRoot.Dispose();
|
||||
|
||||
System.Windows.Forms.Application.Restart();
|
||||
|
||||
Dispatcher.Invoke(Shutdown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using MediaBrowser.IsoMounter;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Updates;
|
||||
@ -59,6 +60,7 @@ using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
@ -84,15 +86,6 @@ namespace MediaBrowser.ServerApplication
|
||||
get { return (IServerConfigurationManager)ConfigurationManager; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the log file prefix.
|
||||
/// </summary>
|
||||
/// <value>The name of the log file prefix.</value>
|
||||
protected override string LogFilePrefixName
|
||||
{
|
||||
get { return "server"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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}/...
|
||||
@ -182,13 +175,6 @@ namespace MediaBrowser.ServerApplication
|
||||
private IItemRepository ItemRepository { get; set; }
|
||||
private INotificationsRepository NotificationsRepository { get; set; }
|
||||
|
||||
public bool IsBackgroundService
|
||||
{
|
||||
get { return _appInterface != null && _appInterface.IsBackgroundService; }
|
||||
}
|
||||
|
||||
private readonly IApplicationInterface _appInterface;
|
||||
|
||||
/// <summary>
|
||||
/// The full path to our startmenu shortcut
|
||||
/// </summary>
|
||||
@ -199,9 +185,15 @@ namespace MediaBrowser.ServerApplication
|
||||
|
||||
private Task<IHttpServer> _httpServerCreationTask;
|
||||
|
||||
public ApplicationHost(IApplicationInterface appInterface)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationHost"/> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationPaths">The application paths.</param>
|
||||
/// <param name="logManager">The log manager.</param>
|
||||
public ApplicationHost(ServerApplicationPaths applicationPaths, ILogManager logManager)
|
||||
: base(applicationPaths, logManager)
|
||||
{
|
||||
_appInterface = appInterface;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -542,7 +534,14 @@ namespace MediaBrowser.ServerApplication
|
||||
Logger.ErrorException("Error sending server restart web socket message", ex);
|
||||
}
|
||||
|
||||
_appInterface.RestartApplication();
|
||||
// Second instance will start first, so release the mutex and dispose the http server ahead of time
|
||||
Application.Current.Dispatcher.Invoke(() => MainStartup.ReleaseMutex(Logger));
|
||||
|
||||
Dispose();
|
||||
|
||||
System.Windows.Forms.Application.Restart();
|
||||
|
||||
ShutdownInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -627,8 +626,7 @@ namespace MediaBrowser.ServerApplication
|
||||
Id = _systemId,
|
||||
ProgramDataPath = ApplicationPaths.ProgramDataPath,
|
||||
MacAddress = GetMacAddress(),
|
||||
HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber,
|
||||
IsBackgroundService = IsBackgroundService
|
||||
HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber
|
||||
};
|
||||
}
|
||||
|
||||
@ -663,7 +661,15 @@ namespace MediaBrowser.ServerApplication
|
||||
Logger.ErrorException("Error sending server shutdown web socket message", ex);
|
||||
}
|
||||
|
||||
_appInterface.ShutdownApplication();
|
||||
ShutdownInternal();
|
||||
}
|
||||
|
||||
public void ShutdownInternal()
|
||||
{
|
||||
Logger.Info("Shutting down application");
|
||||
var app = Application.Current;
|
||||
|
||||
app.Dispatcher.Invoke(app.Shutdown);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,30 +1,40 @@
|
||||
using System.ServiceProcess;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BackgroundService
|
||||
/// </summary>
|
||||
public class BackgroundService : ServiceBase
|
||||
{
|
||||
public BackgroundService()
|
||||
public static string Name = "MediaBrowser";
|
||||
public static string DisplayName = "Media Browser";
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BackgroundService"/> class.
|
||||
/// </summary>
|
||||
public BackgroundService(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
CanPauseAndContinue = false;
|
||||
CanHandleSessionChangeEvent = true;
|
||||
CanStop = false;
|
||||
CanShutdown = true;
|
||||
ServiceName = "Media Browser";
|
||||
|
||||
CanStop = true;
|
||||
|
||||
ServiceName = Name;
|
||||
}
|
||||
|
||||
protected override void OnSessionChange(SessionChangeDescription changeDescription)
|
||||
/// <summary>
|
||||
/// When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
|
||||
/// </summary>
|
||||
protected override void OnStop()
|
||||
{
|
||||
base.OnSessionChange(changeDescription);
|
||||
}
|
||||
_logger.Info("Stop command received");
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnShutdown()
|
||||
{
|
||||
base.OnShutdown();
|
||||
base.OnStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
MediaBrowser.ServerApplication/BackgroundServiceInstaller.cs
Normal file
60
MediaBrowser.ServerApplication/BackgroundServiceInstaller.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
[RunInstaller(true)]
|
||||
public class BackgroundServiceInstaller : System.Configuration.Install.Installer
|
||||
{
|
||||
public BackgroundServiceInstaller()
|
||||
{
|
||||
var process = new ServiceProcessInstaller
|
||||
{
|
||||
Account = ServiceAccount.LocalSystem
|
||||
};
|
||||
|
||||
var serviceAdmin = new ServiceInstaller
|
||||
{
|
||||
StartType = ServiceStartMode.Manual,
|
||||
ServiceName = BackgroundService.Name,
|
||||
DisplayName = BackgroundService.DisplayName,
|
||||
DelayedAutoStart = true,
|
||||
Description = "The windows background service for Media Browser Server."
|
||||
};
|
||||
|
||||
// Microsoft didn't add the ability to add a
|
||||
// description for the services we are going to install
|
||||
// To work around this we'll have to add the
|
||||
// information directly to the registry but I'll leave
|
||||
// this exercise for later.
|
||||
|
||||
// now just add the installers that we created to our
|
||||
// parents container, the documentation
|
||||
// states that there is not any order that you need to
|
||||
// worry about here but I'll still
|
||||
// go ahead and add them in the order that makes sense.
|
||||
Installers.Add(process);
|
||||
Installers.Add(serviceAdmin);
|
||||
}
|
||||
|
||||
protected override void OnBeforeInstall(IDictionary savedState)
|
||||
{
|
||||
Context.Parameters["assemblypath"] = "\"" +
|
||||
Context.Parameters["assemblypath"] + "\" " + GetStartArgs();
|
||||
base.OnBeforeInstall(savedState);
|
||||
}
|
||||
|
||||
protected override void OnBeforeUninstall(IDictionary savedState)
|
||||
{
|
||||
Context.Parameters["assemblypath"] = "\"" +
|
||||
Context.Parameters["assemblypath"] + "\" " + GetStartArgs();
|
||||
base.OnBeforeUninstall(savedState);
|
||||
}
|
||||
|
||||
private string GetStartArgs()
|
||||
{
|
||||
return "-service";
|
||||
}
|
||||
}
|
||||
}
|
@ -64,10 +64,7 @@ namespace MediaBrowser.ServerApplication.EntryPoints
|
||||
{
|
||||
_logger.ErrorException("Error launching startup wizard", ex);
|
||||
|
||||
if (!_appHost.IsBackgroundService)
|
||||
{
|
||||
MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
|
||||
}
|
||||
MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,32 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface IApplicationInterface
|
||||
/// </summary>
|
||||
public interface IApplicationInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is background service.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
|
||||
bool IsBackgroundService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Shutdowns the application.
|
||||
/// </summary>
|
||||
void ShutdownApplication();
|
||||
|
||||
/// <summary>
|
||||
/// Restarts the application.
|
||||
/// </summary>
|
||||
void RestartApplication();
|
||||
|
||||
/// <summary>
|
||||
/// Called when [unhandled exception].
|
||||
/// </summary>
|
||||
/// <param name="ex">The ex.</param>
|
||||
void OnUnhandledException(Exception ex);
|
||||
}
|
||||
}
|
@ -1,12 +1,18 @@
|
||||
using MediaBrowser.Common.Constants;
|
||||
using MediaBrowser.Common.Implementations.Logging;
|
||||
using MediaBrowser.Common.Implementations.Updates;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Server.Implementations;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Configuration.Install;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace MediaBrowser.ServerApplication
|
||||
{
|
||||
@ -17,7 +23,9 @@ namespace MediaBrowser.ServerApplication
|
||||
/// </summary>
|
||||
private static Mutex _singleInstanceMutex;
|
||||
|
||||
private static IApplicationInterface _applicationInterface;
|
||||
private static ApplicationHost _appHost;
|
||||
|
||||
private static App _app;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the entry point of the application.
|
||||
@ -25,6 +33,50 @@ namespace MediaBrowser.ServerApplication
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
var startFlag = Environment.GetCommandLineArgs().ElementAtOrDefault(1);
|
||||
var runService = string.Equals(startFlag, "-service", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var appPaths = CreateApplicationPaths(runService);
|
||||
|
||||
var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
|
||||
logManager.ReloadLogger(LogSeverity.Info);
|
||||
|
||||
var logger = logManager.GetLogger("Main");
|
||||
|
||||
BeginLog(logger);
|
||||
|
||||
// Install directly
|
||||
if (string.Equals(startFlag, "-installservice", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.Info("Performing service installation");
|
||||
InstallService(logger);
|
||||
return;
|
||||
}
|
||||
|
||||
// Restart with admin rights, then install
|
||||
if (string.Equals(startFlag, "-launchinstallservice", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.Info("Performing service installation");
|
||||
RunServiceInstallation();
|
||||
return;
|
||||
}
|
||||
|
||||
// Uninstall directly
|
||||
if (string.Equals(startFlag, "-uninstallservice", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.Info("Performing service uninstallation");
|
||||
UninstallService(logger);
|
||||
return;
|
||||
}
|
||||
|
||||
// Restart with admin rights, then uninstall
|
||||
if (string.Equals(startFlag, "-launchuninstallservice", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.Info("Performing service uninstallation");
|
||||
RunServiceUninstallation();
|
||||
return;
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
|
||||
bool createdNew;
|
||||
@ -36,78 +88,228 @@ namespace MediaBrowser.ServerApplication
|
||||
if (!createdNew)
|
||||
{
|
||||
_singleInstanceMutex = null;
|
||||
logger.Info("Shutting down because another instance of Media Browser Server is already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for the existence of an update archive
|
||||
var appPaths = new ServerApplicationPaths();
|
||||
var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip");
|
||||
if (File.Exists(updateArchive))
|
||||
if (PerformUpdateIfNeeded(appPaths, logger))
|
||||
{
|
||||
// Update is there - execute update
|
||||
try
|
||||
{
|
||||
new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive);
|
||||
|
||||
// And just let the app exit so it can update
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
|
||||
}
|
||||
logger.Info("Exiting to perform application update.");
|
||||
return;
|
||||
}
|
||||
|
||||
StartApplication();
|
||||
try
|
||||
{
|
||||
RunApplication(appPaths, logManager, runService);
|
||||
}
|
||||
finally
|
||||
{
|
||||
logger.Info("Shutting down");
|
||||
|
||||
ReleaseMutex(logger);
|
||||
|
||||
_appHost.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartApplication()
|
||||
/// <summary>
|
||||
/// Creates the application paths.
|
||||
/// </summary>
|
||||
/// <param name="runAsService">if set to <c>true</c> [run as service].</param>
|
||||
/// <returns>ServerApplicationPaths.</returns>
|
||||
private static ServerApplicationPaths CreateApplicationPaths(bool runAsService)
|
||||
{
|
||||
if (runAsService)
|
||||
{
|
||||
#if (RELEASE)
|
||||
var systemPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
||||
|
||||
var programDataPath = Path.GetDirectoryName(systemPath);
|
||||
|
||||
return new ServerApplicationPaths(programDataPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
return new ServerApplicationPaths();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins the log.
|
||||
/// </summary>
|
||||
/// <param name="logger">The logger.</param>
|
||||
private static void BeginLog(ILogger logger)
|
||||
{
|
||||
logger.Info("Media Browser Server started");
|
||||
logger.Info("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs()));
|
||||
|
||||
logger.Info("Server: {0}", Environment.MachineName);
|
||||
logger.Info("Operating system: {0}", Environment.OSVersion.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the application.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <param name="logManager">The log manager.</param>
|
||||
/// <param name="runService">if set to <c>true</c> [run service].</param>
|
||||
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService)
|
||||
{
|
||||
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
|
||||
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
|
||||
if (commandLineArgs.Length > 1 && commandLineArgs[1].Equals("-service"))
|
||||
_appHost = new ApplicationHost(appPaths, logManager);
|
||||
|
||||
_app = new App(_appHost, _appHost.LogManager.GetLogger("App"), runService);
|
||||
|
||||
if (runService)
|
||||
{
|
||||
// Start application as a service
|
||||
StartBackgroundService();
|
||||
_app.AppStarted += (sender, args) => StartService(logManager);
|
||||
}
|
||||
else
|
||||
|
||||
_app.Run();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the service.
|
||||
/// </summary>
|
||||
private static void StartService(ILogManager logManager)
|
||||
{
|
||||
var ctl = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == BackgroundService.Name);
|
||||
|
||||
if (ctl == null)
|
||||
{
|
||||
StartWpfApp();
|
||||
RunServiceInstallation();
|
||||
}
|
||||
|
||||
var service = new BackgroundService(logManager.GetLogger("Service"));
|
||||
|
||||
service.Disposed += service_Disposed;
|
||||
|
||||
ServiceBase.Run(service);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the Disposed event of the service control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
|
||||
static async void service_Disposed(object sender, EventArgs e)
|
||||
{
|
||||
await _appHost.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installs the service.
|
||||
/// </summary>
|
||||
private static void InstallService(ILogger logger)
|
||||
{
|
||||
var runningPath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
try
|
||||
{
|
||||
ManagedInstallerClass.InstallHelper(new[] { runningPath });
|
||||
|
||||
logger.Info("Service installation succeeded");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.ErrorException("Uninstall failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uninstalls the service.
|
||||
/// </summary>
|
||||
private static void UninstallService(ILogger logger)
|
||||
{
|
||||
var runningPath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
try
|
||||
{
|
||||
ManagedInstallerClass.InstallHelper(new[] { "/u", runningPath });
|
||||
|
||||
logger.Info("Service uninstallation succeeded");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.ErrorException("Uninstall failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the service installation.
|
||||
/// </summary>
|
||||
private static void RunServiceInstallation()
|
||||
{
|
||||
var runningPath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = runningPath,
|
||||
|
||||
Arguments = "-installservice",
|
||||
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
Verb = "runas",
|
||||
ErrorDialog = false
|
||||
};
|
||||
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
process.WaitForExit();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the service uninstallation.
|
||||
/// </summary>
|
||||
private static void RunServiceUninstallation()
|
||||
{
|
||||
var runningPath = Process.GetCurrentProcess().MainModule.FileName;
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = runningPath,
|
||||
|
||||
Arguments = "-uninstallservice",
|
||||
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
Verb = "runas",
|
||||
ErrorDialog = false
|
||||
};
|
||||
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
process.WaitForExit();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the SessionEnding event of the SystemEvents control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="SessionEndingEventArgs"/> instance containing the event data.</param>
|
||||
static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
|
||||
{
|
||||
// Try to shutdown gracefully
|
||||
if (_applicationInterface != null)
|
||||
{
|
||||
_applicationInterface.ShutdownApplication();
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartWpfApp()
|
||||
{
|
||||
var app = new App();
|
||||
|
||||
_applicationInterface = app;
|
||||
|
||||
app.Run();
|
||||
}
|
||||
|
||||
private static void StartBackgroundService()
|
||||
{
|
||||
|
||||
var task = _appHost.Shutdown();
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the UnhandledException event of the CurrentDomain control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="UnhandledExceptionEventArgs"/> instance containing the event data.</param>
|
||||
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var exception = (Exception)e.ExceptionObject;
|
||||
|
||||
if (_applicationInterface != null)
|
||||
{
|
||||
_applicationInterface.OnUnhandledException(exception);
|
||||
}
|
||||
_app.OnUnhandledException(exception);
|
||||
|
||||
if (!Debugger.IsAttached)
|
||||
{
|
||||
@ -118,17 +320,50 @@ namespace MediaBrowser.ServerApplication
|
||||
/// <summary>
|
||||
/// Releases the mutex.
|
||||
/// </summary>
|
||||
internal static void ReleaseMutex()
|
||||
internal static void ReleaseMutex(ILogger logger)
|
||||
{
|
||||
if (_singleInstanceMutex == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Debug("Releasing mutex");
|
||||
|
||||
_singleInstanceMutex.ReleaseMutex();
|
||||
_singleInstanceMutex.Close();
|
||||
_singleInstanceMutex.Dispose();
|
||||
_singleInstanceMutex = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the update if needed.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger)
|
||||
{
|
||||
// Look for the existence of an update archive
|
||||
var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip");
|
||||
if (File.Exists(updateArchive))
|
||||
{
|
||||
logger.Info("An update is available from {0}", updateArchive);
|
||||
|
||||
// Update is there - execute update
|
||||
try
|
||||
{
|
||||
new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive);
|
||||
|
||||
// And just let the app exit so it can update
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using MahApps.Metro.Controls;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
@ -173,6 +173,7 @@
|
||||
<HintPath>..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.SQLite, Version=1.0.88.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
@ -209,8 +210,10 @@
|
||||
</Compile>
|
||||
<Compile Include="EntryPoints\StartupWizard.cs" />
|
||||
<Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
|
||||
<Compile Include="IApplicationInterface.cs" />
|
||||
<Compile Include="MainStartup.cs" />
|
||||
<Compile Include="BackgroundServiceInstaller.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Splash\SplashWindow.xaml.cs">
|
||||
<DependentUpon>SplashWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
Loading…
Reference in New Issue
Block a user