mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Application: Allowing the app to be restarted
This commit is contained in:
parent
d5e1d4f003
commit
4daa1eb28d
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -107,25 +107,22 @@ jobs:
|
||||
- name: Create the package structure
|
||||
run: |
|
||||
sudo mkdir -p pkg/usr/lib/
|
||||
sudo mkdir -p pkg/DEBIAN
|
||||
sudo cp -r --no-preserve ownership dist pkg/usr/lib/kyoo
|
||||
sudo install -Dm 644 deployment/kyoo.service -t pkg/usr/lib/systemd/system/
|
||||
sudo install -Dm 644 deployment/kyoo.sysusers pkg/usr/lib/sysusers.d/kyoo.conf
|
||||
sudo install -Dm 644 deployment/kyoo.tmpfiles pkg/usr/lib/tmpfiles.d/kyoo.conf
|
||||
sudo install -Dm 755 deployment/postinst -t pkg/DEBIAN/
|
||||
- uses: jiro4989/build-deb-action@v2
|
||||
with:
|
||||
package: kyoo
|
||||
package_root: pkg
|
||||
maintainer: Zoe Roux <zoe.roux@sdg.moe>
|
||||
version: ${{env.version}}
|
||||
depends: "postgresql, libavutil-dev, libavcodec-dev, libavformat-dev"
|
||||
depends: "libavutil-dev, libavcodec-dev, libavformat-dev"
|
||||
arch: amd64
|
||||
desc: ${{env.description}}
|
||||
- name: Build rpm package
|
||||
run: |
|
||||
mkdir out
|
||||
sudo rm -rf pkg/DEBIAN
|
||||
rpmbuild -bb --buildroot $(pwd)/out --build-in-place --define "_rpmdir $(pwd)/rpm" deployment/kyoo.spec
|
||||
- name: Prepare arch package
|
||||
run: |
|
||||
|
24
Kyoo.Abstractions/Controllers/IApplication.cs
Normal file
24
Kyoo.Abstractions/Controllers/IApplication.cs
Normal file
@ -0,0 +1,24 @@
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that allow one to interact with the host and shutdown or restart the app.
|
||||
/// </summary>
|
||||
public interface IApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Shutdown the process and stop gracefully.
|
||||
/// </summary>
|
||||
void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Restart Kyoo from scratch, reload plugins, configurations and restart the web server.
|
||||
/// </summary>
|
||||
void Restart();
|
||||
|
||||
/// <summary>
|
||||
/// Get the data directory
|
||||
/// </summary>
|
||||
/// <returns>Retrieve the data directory where runtime data should be stored</returns>
|
||||
string GetDataDirectory();
|
||||
}
|
||||
}
|
@ -17,4 +17,5 @@
|
||||
<Target Name="Clean" />
|
||||
<Target Name="Pack" />
|
||||
<Target Name="Restore" />
|
||||
<Target Name="Publish" />
|
||||
</Project>
|
@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Kyoo/Kyoo.csproj"/>
|
||||
<ProjectReference Include="../Kyoo/Kyoo.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Kyoo.WindowsHost
|
||||
{
|
||||
@ -13,22 +10,13 @@ namespace Kyoo.WindowsHost
|
||||
/// It adds a system trait for windows and since the host is build as a windows executable instead of a console
|
||||
/// app, the console is not showed.
|
||||
/// </summary>
|
||||
public static async Task Main(string[] args)
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
object dataDir = Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\SDG\Kyoo\Settings", "DataDir", null)
|
||||
?? Registry.GetValue(@"HKEY_CURRENT_USER\Software\SDG\Kyoo\Settings", "DataDir", null);
|
||||
if (dataDir is string data)
|
||||
Environment.SetEnvironmentVariable("KYOO_DATA_DIR", data);
|
||||
Kyoo.Program.SetupDataDir(args);
|
||||
|
||||
IHost host = Kyoo.Program.CreateWebHostBuilder(args)
|
||||
.ConfigureContainer<ContainerBuilder>(builder =>
|
||||
{
|
||||
builder.RegisterType<SystemTrait>().As<IStartable>().SingleInstance();
|
||||
})
|
||||
.Build();
|
||||
|
||||
await Kyoo.Program.StartWithHost(host);
|
||||
Application application = new();
|
||||
return application.Start(args, builder =>
|
||||
{
|
||||
builder.RegisterType<SystemTrait>().As<IStartable>().SingleInstance();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using Autofac;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Models.Options;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@ -15,6 +16,11 @@ namespace Kyoo.WindowsHost
|
||||
/// </summary>
|
||||
public sealed class SystemTrait : IStartable, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The application running Kyoo.
|
||||
/// </summary>
|
||||
private readonly IApplication _application;
|
||||
|
||||
/// <summary>
|
||||
/// The options containing the <see cref="BasicOptions.PublicUrl"/>.
|
||||
/// </summary>
|
||||
@ -29,16 +35,18 @@ namespace Kyoo.WindowsHost
|
||||
/// <summary>
|
||||
/// Create a new <see cref="SystemTrait"/>.
|
||||
/// </summary>
|
||||
/// <param name="application">The application running Kyoo.</param>
|
||||
/// <param name="options">The options to use.</param>
|
||||
public SystemTrait(IOptions<BasicOptions> options)
|
||||
public SystemTrait(IApplication application, IOptions<BasicOptions> options)
|
||||
{
|
||||
_application = application;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Start()
|
||||
{
|
||||
_thread = new Thread(() => InternalSystemTrait.Run(_options))
|
||||
_thread = new Thread(() => InternalSystemTrait.Run(_application, _options))
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
@ -48,8 +56,7 @@ namespace Kyoo.WindowsHost
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// TODO not sure that the trait is ended and that it does shutdown but the only way to shutdown the
|
||||
// app anyway is via the Trait's Exit or a Signal so it's fine.
|
||||
System.Windows.Forms.Application.Exit();
|
||||
_thread?.Join();
|
||||
_thread = null;
|
||||
}
|
||||
@ -61,6 +68,11 @@ namespace Kyoo.WindowsHost
|
||||
private class InternalSystemTrait : ApplicationContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The application running Kyoo.
|
||||
/// </summary>
|
||||
private readonly IApplication _application;
|
||||
|
||||
/// <summary>
|
||||
/// The options containing the <see cref="BasicOptions.PublicUrl"/>.
|
||||
/// </summary>
|
||||
private readonly IOptions<BasicOptions> _options;
|
||||
@ -73,13 +85,15 @@ namespace Kyoo.WindowsHost
|
||||
/// <summary>
|
||||
/// Create a new <see cref="InternalSystemTrait"/>. Used only by <see cref="Run"/>.
|
||||
/// </summary>
|
||||
/// <param name="application">The application running Kyoo.</param>
|
||||
/// <param name="options">The option containing the public url.</param>
|
||||
private InternalSystemTrait(IOptions<BasicOptions> options)
|
||||
private InternalSystemTrait(IApplication application, IOptions<BasicOptions> options)
|
||||
{
|
||||
_application = application;
|
||||
_options = options;
|
||||
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => Dispose();
|
||||
Application.ApplicationExit += (_, _) => Dispose();
|
||||
System.Windows.Forms.Application.ApplicationExit += (_, _) => Dispose();
|
||||
|
||||
_icon = new NotifyIcon();
|
||||
_icon.Text = "Kyoo";
|
||||
@ -96,20 +110,21 @@ namespace Kyoo.WindowsHost
|
||||
_icon.ContextMenuStrip.Items.AddRange(new ToolStripItem[]
|
||||
{
|
||||
new ToolStripMenuItem("Open browser", null, (_, _) => { _StartBrowser(); }),
|
||||
new ToolStripMenuItem("Open logs", null, (_, _) => { Process.Start("explorer.exe", Environment.CurrentDirectory); }),
|
||||
new ToolStripMenuItem("Open logs", null, (_, _) => { _OpenLogs(); }),
|
||||
new ToolStripSeparator(),
|
||||
new ToolStripMenuItem("Exit", null, (_, _) => { Environment.Exit(0); })
|
||||
new ToolStripMenuItem("Exit", null, (_, _) => { _application.Shutdown(); })
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run the trait in the current thread, this method does not return while the trait is running.
|
||||
/// </summary>
|
||||
/// <param name="application">The application running Kyoo.</param>
|
||||
/// <param name="options">The options to pass to <see cref="InternalSystemTrait"/>.</param>
|
||||
public static void Run(IOptions<BasicOptions> options)
|
||||
public static void Run(IApplication application, IOptions<BasicOptions> options)
|
||||
{
|
||||
using InternalSystemTrait trait = new(options);
|
||||
Application.Run(trait);
|
||||
using InternalSystemTrait trait = new(application, options);
|
||||
System.Windows.Forms.Application.Run(trait);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -134,6 +149,15 @@ namespace Kyoo.WindowsHost
|
||||
};
|
||||
browser.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the log directory in windows's explorer.
|
||||
/// </summary>
|
||||
private void _OpenLogs()
|
||||
{
|
||||
string logDir = Path.Combine(_application.GetDataDirectory(), "logs");
|
||||
Process.Start("explorer.exe", logDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
244
Kyoo/Application.cs
Normal file
244
Kyoo/Application.cs
Normal file
@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Win32;
|
||||
using Serilog;
|
||||
using Serilog.Templates;
|
||||
using Serilog.Templates.Themes;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
public class Application : IApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to the data directory.
|
||||
/// </summary>
|
||||
private string _dataDir;
|
||||
|
||||
/// <summary>
|
||||
/// Should the application restart after a shutdown?
|
||||
/// </summary>
|
||||
private bool _shouldRestart;
|
||||
|
||||
/// <summary>
|
||||
/// The cancellation token source used to allow the app to be shutdown or restarted.
|
||||
/// </summary>
|
||||
private CancellationTokenSource _tokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// Start the application with the given console args.
|
||||
/// This is generally called from the Main entrypoint of Kyoo.
|
||||
/// </summary>
|
||||
/// <param name="args">The console arguments to use for kyoo.</param>
|
||||
/// <returns>A task representing the whole process</returns>
|
||||
public Task Start(string[] args)
|
||||
{
|
||||
return Start(args, _ => { });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the application with the given console args.
|
||||
/// This is generally called from the Main entrypoint of Kyoo.
|
||||
/// </summary>
|
||||
/// <param name="args">The console arguments to use for kyoo.</param>
|
||||
/// <param name="configure">A custom action to configure the container before the start</param>
|
||||
/// <returns>A task representing the whole process</returns>
|
||||
public async Task Start(string[] args, Action<ContainerBuilder> configure)
|
||||
{
|
||||
_dataDir = _SetupDataDir(args);
|
||||
|
||||
LoggerConfiguration config = new();
|
||||
_ConfigureLogging(config, null);
|
||||
Log.Logger = config.CreateBootstrapLogger()
|
||||
.ForContext<Application>();
|
||||
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush();
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, ex)
|
||||
=> Log.Fatal(ex.ExceptionObject as Exception, "Unhandled exception");
|
||||
|
||||
do
|
||||
{
|
||||
IHost host = _CreateWebHostBuilder(args)
|
||||
.ConfigureContainer(configure)
|
||||
.Build();
|
||||
Log.Logger = host.Services.GetRequiredService<ILogger>().ForContext<Application>();
|
||||
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
await _StartWithHost(host, _tokenSource.Token);
|
||||
}
|
||||
while (_shouldRestart);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Shutdown()
|
||||
{
|
||||
_shouldRestart = false;
|
||||
_tokenSource.Cancel();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Restart()
|
||||
{
|
||||
_shouldRestart = true;
|
||||
_tokenSource.Cancel();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetDataDirectory()
|
||||
{
|
||||
return _dataDir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the data directory from environment variables and command line arguments, create it if necessary.
|
||||
/// Set the current directory to said data folder and place a default configuration file if it does not already
|
||||
/// exists.
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments</param>
|
||||
/// <returns>The current data directory.</returns>
|
||||
private static string _SetupDataDir(string[] args)
|
||||
{
|
||||
Dictionary<string, string> registry = new();
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
object dataDir = Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\SDG\Kyoo\Settings", "DataDir", null)
|
||||
?? Registry.GetValue(@"HKEY_CURRENT_USER\Software\SDG\Kyoo\Settings", "DataDir", null);
|
||||
if (dataDir is string data)
|
||||
registry.Add("DataDir", data);
|
||||
}
|
||||
|
||||
IConfiguration parsed = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(registry)
|
||||
.AddEnvironmentVariables()
|
||||
.AddEnvironmentVariables("KYOO_")
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
|
||||
string path = parsed.GetValue<string>("datadir")
|
||||
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Kyoo");
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
Environment.CurrentDirectory = path;
|
||||
|
||||
if (!File.Exists(Program.JsonConfigPath))
|
||||
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, Program.JsonConfigPath),
|
||||
Program.JsonConfigPath);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the given host and log failing exceptions.
|
||||
/// </summary>
|
||||
/// <param name="host">The host to start.</param>
|
||||
/// <param name="cancellationToken">A token to allow one to stop the host.</param>
|
||||
private async Task _StartWithHost(IHost host, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Information("Running as {Name}", Environment.UserName);
|
||||
Log.Information("Data directory: {DataDirectory}", GetDataDirectory());
|
||||
await host.RunAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Unhandled exception");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a a web host
|
||||
/// </summary>
|
||||
/// <param name="args">Command line parameters that can be handled by kestrel</param>
|
||||
/// <returns>A new web host instance</returns>
|
||||
private IHostBuilder _CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
IConfiguration configuration = _SetupConfig(new ConfigurationBuilder(), args).Build();
|
||||
|
||||
return new HostBuilder()
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
|
||||
.UseEnvironment(Program.Environment)
|
||||
.ConfigureAppConfiguration(x => _SetupConfig(x, args))
|
||||
.UseSerilog((host, builder) => _ConfigureLogging(builder, host.Configuration))
|
||||
.ConfigureServices(x => x.AddRouting())
|
||||
.ConfigureContainer<ContainerBuilder>(x =>
|
||||
{
|
||||
x.RegisterInstance(this).As<IApplication>().SingleInstance().ExternallyOwned();
|
||||
})
|
||||
.ConfigureWebHost(x => x
|
||||
.UseKestrel(options => { options.AddServerHeader = false; })
|
||||
.UseIIS()
|
||||
.UseIISIntegration()
|
||||
.UseUrls(configuration.GetValue<string>("basics:url"))
|
||||
.UseStartup(host => PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog()))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register settings.json, environment variables and command lines arguments as configuration.
|
||||
/// </summary>
|
||||
/// <param name="builder">The configuration builder to use</param>
|
||||
/// <param name="args">The command line arguments</param>
|
||||
/// <returns>The modified configuration builder</returns>
|
||||
private IConfigurationBuilder _SetupConfig(IConfigurationBuilder builder, string[] args)
|
||||
{
|
||||
return builder.SetBasePath(GetDataDirectory())
|
||||
.AddJsonFile(Path.Join(AppDomain.CurrentDomain.BaseDirectory, Program.JsonConfigPath), false, true)
|
||||
.AddJsonFile(Program.JsonConfigPath, false, true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddEnvironmentVariables("KYOO_")
|
||||
.AddCommandLine(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the logging.
|
||||
/// </summary>
|
||||
/// <param name="builder">The logger builder to configure.</param>
|
||||
/// <param name="configuration">The configuration to read settings from.</param>
|
||||
private void _ConfigureLogging(LoggerConfiguration builder, [CanBeNull] IConfiguration configuration)
|
||||
{
|
||||
if (configuration != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.ReadFrom.Configuration(configuration, "logging");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Could not read serilog configuration");
|
||||
}
|
||||
}
|
||||
|
||||
const string template =
|
||||
"[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 15} "
|
||||
+ "({@i:0000000000})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}";
|
||||
|
||||
builder
|
||||
.WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code))
|
||||
.WriteTo.Debug()
|
||||
.WriteTo.File(
|
||||
path: Path.Combine(GetDataDirectory(), "logs", "log-.log"),
|
||||
formatter: new ExpressionTemplate(template),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
rollOnFileSizeLimit: true
|
||||
)
|
||||
.Enrich.WithThreadId()
|
||||
.Enrich.FromLogContext();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,10 +21,6 @@
|
||||
<TranscoderBinary Condition="$(IsWindows) == true">transcoder.dll</TranscoderBinary>
|
||||
<TranscoderBinary Condition="$(IsOSX) == true">libtranscoder.dylib</TranscoderBinary>
|
||||
<TranscoderBinary Condition="$(IsLinux) == true">libtranscoder.so</TranscoderBinary>
|
||||
|
||||
<TranscoderBuild Condition="$(IsWindows) == true">$(TranscoderRoot)buildWin</TranscoderBuild>
|
||||
<TranscoderBuild Condition="$(IsOSX) == true">$(TranscoderRoot)buildOSX</TranscoderBuild>
|
||||
<TranscoderBuild Condition="$(IsLinux) == true">$(TranscoderRoot)build</TranscoderBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -52,7 +48,7 @@
|
||||
<ProjectReference Include="../Kyoo.WebApp/Kyoo.WebApp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true' And !Exists('$(TranscoderBuild)/$(TranscoderBinary)')">
|
||||
<Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true' And !Exists('$(TranscoderRoot)/build/$(TranscoderBinary)')">
|
||||
<Exec Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'" />
|
||||
<Exec Command="(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G "NMake Makefiles" %26%26 nmake" WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'" />
|
||||
</Target>
|
||||
|
159
Kyoo/Program.cs
159
Kyoo/Program.cs
@ -1,17 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Serilog.Templates;
|
||||
using Serilog.Templates.Themes;
|
||||
using SEnvironment = System.Environment;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
@ -29,158 +17,19 @@ namespace Kyoo
|
||||
/// The string representation of the environment used in <see cref="IWebHostEnvironment"/>.
|
||||
/// </summary>
|
||||
#if DEBUG
|
||||
private const string Environment = "Development";
|
||||
public const string Environment = "Development";
|
||||
#else
|
||||
private const string Environment = "Production";
|
||||
public const string Environment = "Production";
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the bootstrap logger to use it anywhere. This is done here so it is called before any method,
|
||||
/// even if the <see cref="Main"/> is not used and this binary is used as a dll.
|
||||
/// </summary>
|
||||
static Program()
|
||||
{
|
||||
LoggerConfiguration config = new();
|
||||
_ConfigureLogging(null, config);
|
||||
Log.Logger = config.CreateBootstrapLogger().ForContext<Application>();
|
||||
|
||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main function of the program
|
||||
/// </summary>
|
||||
/// <param name="args">Command line arguments</param>
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
SetupDataDir(args);
|
||||
return StartWithHost(CreateWebHostBuilder(args).Build());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the given host and log failing exceptions.
|
||||
/// </summary>
|
||||
/// <param name="host">The host to start.</param>
|
||||
public static async Task StartWithHost(IHost host)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Information("Running as {Name}", System.Environment.UserName);
|
||||
Log.Information("Data directory: {DataDirectory}", System.Environment.CurrentDirectory);
|
||||
await host.RunAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Unhandled exception");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register settings.json, environment variables and command lines arguments as configuration.
|
||||
/// </summary>
|
||||
/// <param name="builder">The configuration builder to use</param>
|
||||
/// <param name="args">The command line arguments</param>
|
||||
/// <returns>The modified configuration builder</returns>
|
||||
private static IConfigurationBuilder SetupConfig(IConfigurationBuilder builder, string[] args)
|
||||
{
|
||||
return builder.SetBasePath(System.Environment.CurrentDirectory)
|
||||
.AddJsonFile(Path.Join(AppDomain.CurrentDomain.BaseDirectory, JsonConfigPath), false, true)
|
||||
.AddJsonFile(JsonConfigPath, false, true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddEnvironmentVariables("KYOO_")
|
||||
.AddCommandLine(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the logging.
|
||||
/// </summary>
|
||||
/// <param name="context">The host context that contains the configuration</param>
|
||||
/// <param name="builder">The logger builder to configure.</param>
|
||||
private static void _ConfigureLogging([CanBeNull] HostBuilderContext context, LoggerConfiguration builder)
|
||||
{
|
||||
if (context != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.ReadFrom.Configuration(context.Configuration, "logging");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Could not read serilog configuration");
|
||||
}
|
||||
}
|
||||
|
||||
const string template =
|
||||
"[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 15} "
|
||||
+ "({@i:0000000000})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}";
|
||||
|
||||
builder
|
||||
.WriteTo.Console(new ExpressionTemplate(template, theme: TemplateTheme.Code))
|
||||
.WriteTo.Debug()
|
||||
.WriteTo.File(
|
||||
path: "logs/log-.log",
|
||||
formatter: new ExpressionTemplate(template),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
rollOnFileSizeLimit: true
|
||||
)
|
||||
.Enrich.WithThreadId()
|
||||
.Enrich.FromLogContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a a web host
|
||||
/// </summary>
|
||||
/// <param name="args">Command line parameters that can be handled by kestrel</param>
|
||||
/// <returns>A new web host instance</returns>
|
||||
public static IHostBuilder CreateWebHostBuilder(string[] args)
|
||||
{
|
||||
IConfiguration configuration = SetupConfig(new ConfigurationBuilder(), args).Build();
|
||||
|
||||
return new HostBuilder()
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
|
||||
.UseEnvironment(Environment)
|
||||
.ConfigureAppConfiguration(x => SetupConfig(x, args))
|
||||
.UseSerilog(_ConfigureLogging)
|
||||
.ConfigureServices(x => x.AddRouting())
|
||||
.ConfigureWebHost(x => x
|
||||
.UseKestrel(options => { options.AddServerHeader = false; })
|
||||
.UseIIS()
|
||||
.UseIISIntegration()
|
||||
.UseUrls(configuration.GetValue<string>("basics:url"))
|
||||
.UseStartup(host => PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog()))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the data directory from environment variables and command line arguments, create it if necessary.
|
||||
/// Set the current directory to said data folder and place a default configuration file if it does not already
|
||||
/// exists.
|
||||
/// </summary>
|
||||
/// <param name="args">The command line arguments</param>
|
||||
public static void SetupDataDir(string[] args)
|
||||
{
|
||||
IConfiguration parsed = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.AddEnvironmentVariables("KYOO_")
|
||||
.AddCommandLine(args)
|
||||
.Build();
|
||||
|
||||
string path = parsed.GetValue<string>("data_dir");
|
||||
if (path == null)
|
||||
path = Path.Combine(SEnvironment.GetFolderPath(SEnvironment.SpecialFolder.LocalApplicationData), "Kyoo");
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
SEnvironment.CurrentDirectory = path;
|
||||
|
||||
if (!File.Exists(JsonConfigPath))
|
||||
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, JsonConfigPath), JsonConfigPath);
|
||||
Application application = new();
|
||||
return application.Start(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An useless class only used to have a logger in the main.
|
||||
/// </summary>
|
||||
internal class Application {}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ arch=("i686" "x86_64" "armv6h")
|
||||
url="https://github.com/AnonymusRaccoon/Kyoo"
|
||||
license=("GPLv3")
|
||||
groups=()
|
||||
depends=("dotnet-runtime>=5" "aspnet-runtime>=5" "postgresql" "ffmpeg")
|
||||
depends=("dotnet-runtime>=5" "aspnet-runtime>=5" "ffmpeg")
|
||||
makedepends=("dotnet-sdk>=5" "cmake" "gcc" "make" "npm" "git")
|
||||
install="kyoo.install"
|
||||
source=("git+https://github.com/AnonymusRaccoon/Kyoo" #tag=v${pkgver}
|
||||
@ -30,7 +30,7 @@ build() {
|
||||
# cd "Kyoo-$pkgver"
|
||||
cd "Kyoo"
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
dotnet publish -c Release -o "$srcdir/output" Kyoo
|
||||
dotnet publish -c Release -o "$srcdir/output" Kyoo
|
||||
}
|
||||
|
||||
package() {
|
||||
|
@ -8,7 +8,7 @@ arch=("i686" "x86_64" "armv6h")
|
||||
url="https://github.com/AnonymusRaccoon/Kyoo"
|
||||
license=("GPLv3")
|
||||
groups=()
|
||||
depends=("postgresql" "ffmpeg")
|
||||
depends=("ffmpeg")
|
||||
makedepends=()
|
||||
install="kyoo.install"
|
||||
# The output folder is needed but we can't use directory in the source array.
|
||||
|
@ -1,11 +0,0 @@
|
||||
post_install() {
|
||||
sudo -u postgres psql <<- "EOF"
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE ROLE kyoo WITH CREATEDB LOGIN PASSWORD 'kyooPassword';
|
||||
EXCEPTION WHEN DUPLICATE_OBJECT THEN
|
||||
RAISE NOTICE 'not creating role kyoo -- it already exists';
|
||||
END
|
||||
$$;
|
||||
EOF
|
||||
}
|
@ -5,7 +5,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
User=kyoo
|
||||
Environment=KYOO_DATA_DIR=/var/lib/kyoo
|
||||
Environment=KYOO_DATADIR=/var/lib/kyoo
|
||||
ExecStart=/usr/lib/kyoo/Kyoo
|
||||
Restart=on-abort
|
||||
TimeoutSec=20
|
||||
|
@ -7,7 +7,7 @@ Summary: A media browser
|
||||
URL: https://github.com/AnonymusRaccoon/Kyoo
|
||||
License: GPL-3.0
|
||||
BuildArch: x86_64
|
||||
Requires: postgresql-server ffmpeg-devel
|
||||
Requires: ffmpeg-devel
|
||||
AutoReqProv: no
|
||||
|
||||
%description
|
||||
@ -24,17 +24,3 @@ rm -rf %{buildroot}
|
||||
/usr/lib/systemd/system/*
|
||||
/usr/lib/sysusers.d/kyoo.conf
|
||||
/usr/lib/tmpfiles.d/kyoo.conf
|
||||
|
||||
%post
|
||||
sudo postgresql-setup --initdb 2> /dev/null || true
|
||||
sudo systemctl start postgresql
|
||||
sudo -u postgres psql << "EOF"
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE ROLE kyoo WITH CREATEDB LOGIN PASSWORD 'kyooPassword';
|
||||
EXCEPTION WHEN DUPLICATE_OBJECT THEN
|
||||
RAISE NOTICE 'not creating role kyoo -- it already exists';
|
||||
END
|
||||
$$;
|
||||
EOF
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
sudo -u postgres psql << "EOF"
|
||||
DO $$
|
||||
BEGIN
|
||||
CREATE ROLE kyoo WITH CREATEDB LOGIN PASSWORD 'kyooPassword';
|
||||
EXCEPTION WHEN DUPLICATE_OBJECT THEN
|
||||
RAISE NOTICE 'not creating role kyoo -- it already exists';
|
||||
END
|
||||
$$;
|
||||
EOF
|
||||
|
||||
systemd-sysusers
|
||||
systemd-tmpfiles --create
|
Loading…
x
Reference in New Issue
Block a user