Build: Removing the need of symlink, cleaning up plugin enable state and fixing main logger

This commit is contained in:
Zoe Roux 2021-09-01 17:29:52 +02:00
parent 87244df497
commit 186a2d15ca
9 changed files with 100 additions and 63 deletions

View File

@ -93,14 +93,5 @@ namespace Kyoo.Abstractions.Controllers
{ {
// Skipped // Skipped
} }
/// <summary>
/// An optional callback function called when the startups ends and this plugin has been flagged has disabled.
/// It allow a plugin to log an error or warning message to inform why it has been disabled.
/// </summary>
void Disabled()
{
// Skipped
}
} }
} }

View File

@ -42,6 +42,11 @@ namespace Kyoo.Core
/// </summary> /// </summary>
private readonly string _environment; private readonly string _environment;
/// <summary>
/// The logger used for startup and error messages.
/// </summary>
private ILogger _logger;
/// <summary> /// <summary>
/// Create a new <see cref="Application"/> that will use the specified environment. /// Create a new <see cref="Application"/> that will use the specified environment.
@ -76,9 +81,9 @@ namespace Kyoo.Core
_dataDir = _SetupDataDir(args); _dataDir = _SetupDataDir(args);
LoggerConfiguration config = new(); LoggerConfiguration config = new();
_ConfigureLogging(config, null); _ConfigureLogging(config, null, null);
Log.Logger = config.CreateBootstrapLogger() Log.Logger = config.CreateBootstrapLogger();
.ForContext<Application>(); _logger = Log.Logger.ForContext<Application>();
AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush(); AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush();
AppDomain.CurrentDomain.UnhandledException += (_, ex) AppDomain.CurrentDomain.UnhandledException += (_, ex)
@ -89,7 +94,6 @@ namespace Kyoo.Core
IHost host = _CreateWebHostBuilder(args) IHost host = _CreateWebHostBuilder(args)
.ConfigureContainer(configure) .ConfigureContainer(configure)
.Build(); .Build();
Log.Logger = host.Services.GetRequiredService<ILogger>().ForContext<Application>();
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
await _StartWithHost(host, _tokenSource.Token); await _StartWithHost(host, _tokenSource.Token);
@ -173,13 +177,13 @@ namespace Kyoo.Core
{ {
try try
{ {
Log.Information("Running as {Name}", Environment.UserName); _logger.Information("Running as {Name}", Environment.UserName);
Log.Information("Data directory: {DataDirectory}", GetDataDirectory()); _logger.Information("Data directory: {DataDirectory}", GetDataDirectory());
await host.RunAsync(cancellationToken); await host.RunAsync(cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Fatal(ex, "Unhandled exception"); _logger.Fatal(ex, "Unhandled exception");
} }
} }
@ -197,7 +201,7 @@ namespace Kyoo.Core
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory) .UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
.UseEnvironment(_environment) .UseEnvironment(_environment)
.ConfigureAppConfiguration(x => _SetupConfig(x, args)) .ConfigureAppConfiguration(x => _SetupConfig(x, args))
.UseSerilog((host, builder) => _ConfigureLogging(builder, host.Configuration)) .UseSerilog((host, services, builder) => _ConfigureLogging(builder, host.Configuration, services))
.ConfigureServices(x => x.AddRouting()) .ConfigureServices(x => x.AddRouting())
.ConfigureContainer<ContainerBuilder>(x => .ConfigureContainer<ContainerBuilder>(x =>
{ {
@ -227,13 +231,16 @@ namespace Kyoo.Core
.AddEnvironmentVariables("KYOO_") .AddEnvironmentVariables("KYOO_")
.AddCommandLine(args); .AddCommandLine(args);
} }
/// <summary> /// <summary>
/// Configure the logging. /// Configure the logging.
/// </summary> /// </summary>
/// <param name="builder">The logger builder to configure.</param> /// <param name="builder">The logger builder to configure.</param>
/// <param name="configuration">The configuration to read settings from.</param> /// <param name="configuration">The configuration to read settings from.</param>
private void _ConfigureLogging(LoggerConfiguration builder, [CanBeNull] IConfiguration configuration) /// <param name="services">The services to read configuration from.</param>
private void _ConfigureLogging(LoggerConfiguration builder,
[CanBeNull] IConfiguration configuration,
[CanBeNull] IServiceProvider services)
{ {
if (configuration != null) if (configuration != null)
{ {
@ -243,10 +250,13 @@ namespace Kyoo.Core
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Fatal(ex, "Could not read serilog configuration"); _logger.Fatal(ex, "Could not read serilog configuration");
} }
} }
if (services != null)
builder.ReadFrom.Services(services);
const string template = const string template =
"[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 15} " "[{@t:HH:mm:ss} {@l:u3} {Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1), 15} "
+ "({@i:0000000000})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}"; + "({@i:0000000000})] {@m}{#if not EndsWith(@m, '\n')}\n{#end}{@x}";

View File

@ -51,14 +51,6 @@ namespace Kyoo.Core.Controllers
_logger = logger; _logger = logger;
} }
public void SetProvider(IServiceProvider provider)
{
// TODO temporary bullshit to inject services before the configure asp net.
// TODO should rework this when the host will be reworked, as well as the asp net configure.
_provider = provider;
}
/// <inheritdoc /> /// <inheritdoc />
public T GetPlugin<T>(string name) public T GetPlugin<T>(string name)
{ {
@ -111,16 +103,13 @@ namespace Kyoo.Core.Controllers
_logger.LogTrace("Loading new plugins..."); _logger.LogTrace("Loading new plugins...");
string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories); string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories);
IPlugin[] newPlugins = plugins _plugins.AddRange(plugins
.Concat(pluginsPaths.SelectMany(LoadPlugin)) .Concat(pluginsPaths.SelectMany(LoadPlugin))
.Where(x => x.Enabled)
.GroupBy(x => x.Name) .GroupBy(x => x.Name)
.Select(x => x.First()) .Select(x => x.First())
.ToArray(); );
_plugins.AddRange(newPlugins.Where(x => x.Enabled));
foreach (IPlugin plugin in newPlugins.Where(x => !x.Enabled))
plugin.Disabled();
if (!_plugins.Any()) if (!_plugins.Any())
_logger.LogInformation("No plugin enabled"); _logger.LogInformation("No plugin enabled");
else else

View File

@ -74,9 +74,9 @@
}, },
"tvdb": { "tvdb": {
"apiKey": "REDACTED" "apiKey": ""
}, },
"the-moviedb": { "the-moviedb": {
"apiKey": "REDACTED" "apiKey": ""
} }
} }

View File

@ -4,6 +4,8 @@ using Autofac;
using Kyoo.Abstractions; using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.TheMovieDb.Models; using Kyoo.TheMovieDb.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Kyoo.TheMovieDb namespace Kyoo.TheMovieDb
{ {
@ -16,17 +18,38 @@ namespace Kyoo.TheMovieDb
public string Slug => "the-moviedb"; public string Slug => "the-moviedb";
/// <inheritdoc /> /// <inheritdoc />
public string Name => "TheMovieDb Provider"; public string Name => "TheMovieDb";
/// <inheritdoc /> /// <inheritdoc />
public string Description => "A metadata provider for TheMovieDB."; public string Description => "A metadata provider for TheMovieDB.";
/// <inheritdoc />
public bool Enabled => !string.IsNullOrEmpty(_configuration.GetValue<string>("the-moviedb:apikey"));
/// <inheritdoc /> /// <inheritdoc />
public Dictionary<string, Type> Configuration => new() public Dictionary<string, Type> Configuration => new()
{ {
{ TheMovieDbOptions.Path, typeof(TheMovieDbOptions) } { TheMovieDbOptions.Path, typeof(TheMovieDbOptions) }
}; };
/// <summary>
/// The configuration used to check if the api key is present or not.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// Create a new <see cref="PluginTmdb"/>.
/// </summary>
/// <param name="configuration">The configuration used to check if the api key is present or not.</param>
/// <param name="logger">The logger used to warn when the api key is not present.</param>
public PluginTmdb(IConfiguration configuration, ILogger<PluginTmdb> logger)
{
_configuration = configuration;
if (!Enabled)
logger.LogWarning("No API key configured for TheMovieDB provider. " +
"To enable TheMovieDB, specify one in the setting the-moviedb:APIKEY ");
}
/// <inheritdoc /> /// <inheritdoc />
public void Configure(ContainerBuilder builder) public void Configure(ContainerBuilder builder)
{ {

View File

@ -10,6 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
<PackageReference Include="TvDbSharper" Version="3.2.2" /> <PackageReference Include="TvDbSharper" Version="3.2.2" />

View File

@ -4,6 +4,8 @@ using Autofac;
using Kyoo.Abstractions; using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.TheTvdb.Models; using Kyoo.TheTvdb.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using TvDbSharper; using TvDbSharper;
namespace Kyoo.TheTvdb namespace Kyoo.TheTvdb
@ -15,19 +17,40 @@ namespace Kyoo.TheTvdb
{ {
/// <inheritdoc /> /// <inheritdoc />
public string Slug => "the-tvdb"; public string Slug => "the-tvdb";
/// <inheritdoc /> /// <inheritdoc />
public string Name => "The TVDB Provider"; public string Name => "TVDB";
/// <inheritdoc /> /// <inheritdoc />
public string Description => "A metadata provider for The TVDB."; public string Description => "A metadata provider for The TVDB.";
/// <inheritdoc />
public bool Enabled => !string.IsNullOrEmpty(_configuration.GetValue<string>("tvdb:apikey"));
/// <inheritdoc /> /// <inheritdoc />
public Dictionary<string, Type> Configuration => new() public Dictionary<string, Type> Configuration => new()
{ {
{ TvdbOption.Path, typeof(TvdbOption) } { TvdbOption.Path, typeof(TvdbOption) }
}; };
/// <summary>
/// The configuration used to check if the api key is present or not.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// Create a new <see cref="PluginTvdb"/>.
/// </summary>
/// <param name="configuration">The configuration used to check if the api key is present or not.</param>
/// <param name="logger">The logger used to warn when the api key is not present.</param>
public PluginTvdb(IConfiguration configuration, ILogger<PluginTvdb> logger)
{
_configuration = configuration;
if (!Enabled)
logger.LogWarning("No API key configured for TVDB provider. " +
"To enable TVDB, specify one in the setting TVDB:APIKEY ");
}
/// <inheritdoc /> /// <inheritdoc />
public void Configure(ContainerBuilder builder) public void Configure(ContainerBuilder builder)
{ {

View File

@ -63,11 +63,6 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
<Target Name="SymlinkViews" AfterTargets="Build" Condition="'$(SkipWebApp)' != 'true' And $(Configuration) == 'Debug'">
<Exec WorkingDirectory="$(ProjectDir)../Kyoo" Command="ln -fs '$(ProjectDir)' $(OutDir)" Condition="$(OS) == 'Unix'" />
<Exec WorkingDirectory="$(ProjectDir)../Kyoo" Command="if not exist %22$(OutDir)$(ProjectName)%22 mklink /D %22$(OutDir)$(ProjectName)%22 %22$(ProjectDir)%22" Condition="$(OS) != 'Unix'" />
</Target>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" /> <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup> <PropertyGroup>

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@ -33,28 +34,17 @@ namespace Kyoo.WebApp
/// <inheritdoc /> /// <inheritdoc />
public bool Enabled => Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")); public bool Enabled => Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"));
/// <summary>
/// A logger only used to inform the user if the webapp could not be enabled.
/// </summary>
private readonly ILogger<WebAppModule> _logger;
/// <summary> /// <summary>
/// Create a new <see cref="WebAppModule"/>. /// Create a new <see cref="WebAppModule"/>.
/// </summary> /// </summary>
/// <param name="logger">A logger only used to inform the user if the webapp could not be enabled.</param> /// <param name="logger">A logger only used to inform the user if the webapp could not be enabled.</param>
public WebAppModule(ILogger<WebAppModule> logger) public WebAppModule(ILogger<WebAppModule> logger)
{ {
_logger = logger; if (!Enabled)
logger.LogError("The web app files could not be found, it will be disabled. " +
"If you cloned the project, you probably forgot to use the --recurse flag");
} }
/// <inheritdoc />
public void Disabled()
{
_logger.LogError("The web app files could not be found, it will be disabled. " +
"If you cloned the project, you probably forgot to use the --recurse flag");
}
/// <inheritdoc /> /// <inheritdoc />
public void Configure(IServiceCollection services) public void Configure(IServiceCollection services)
{ {
@ -99,12 +89,27 @@ namespace Kyoo.WebApp
{ {
app.UseSpa(spa => app.UseSpa(spa =>
{ {
spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp", "Front"); spa.Options.SourcePath = _GetSpaSourcePath();
if (env.IsDevelopment()) if (env.IsDevelopment())
spa.UseAngularCliServer("start"); spa.UseAngularCliServer("start");
}); });
}, SA.Endpoint - 500) }, SA.Endpoint - 500)
}; };
/// <summary>
/// Get the root directory of the web app
/// </summary>
/// <returns>The path of the source code of the web app or null if the directory has been deleted.</returns>
private static string _GetSpaSourcePath()
{
string GetSelfPath([CallerFilePath] string path = null)
{
return path;
}
string path = Path.Join(Path.GetDirectoryName(GetSelfPath()), "Front");
return Directory.Exists(path) ? path : null;
}
} }
} }