Merge pull request #34 from AnonymusRaccoon/startup

Startup
This commit is contained in:
Zoe Roux 2021-08-14 21:08:37 +02:00 committed by GitHub
commit 43d2153bd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
183 changed files with 1469 additions and 1412 deletions

View File

@ -31,12 +31,12 @@ jobs:
workflow: build.yml
path: ./artifacts
github_token: ${{secrets.GITHUB_TOKEN}}
- name: Public the common to nuget
- name: Public the abstractions to nuget
id: publish_nuget
uses: rohith/publish-nuget@v2
with:
PROJECT_FILE_PATH: Kyoo.Common/Kyoo.Common.csproj
PACKAGE_NAME: Kyoo.Common
PROJECT_FILE_PATH: Kyoo.Abstractions/Kyoo.Abstractions.csproj
PACKAGE_NAME: Kyoo.Abstractions
VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
INCLUDE_SYMBOLS: true

View File

@ -24,7 +24,7 @@ jobs:
- name: Build
run: |
dotnet build --no-restore '-p:SkipWebApp=true;SkipTranscoder=true' -p:CopyLocalLockFileAssemblies=true
cp ./Kyoo.Common/bin/Debug/net5.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll ./tests/Kyoo.Tests/bin/Debug/net5.0/
cp ./Kyoo.Abstractions/bin/Debug/net5.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll ./tests/Kyoo.Tests/bin/Debug/net5.0/
- name: Test
run: dotnet test --no-build '-p:CollectCoverage=true;CoverletOutputFormat=opencover'
env:

2
.gitmodules vendored
View File

@ -3,6 +3,6 @@
url = ../Kyoo.Transcoder.git
branch = master
[submodule "WebApp"]
path = Kyoo.WebApp
path = Kyoo.WebApp/Front
url = ../Kyoo.WebApp.git
branch = master

View File

@ -7,6 +7,7 @@ RUN cmake . && make -j
FROM node:alpine as webapp
WORKDIR /webapp
COPY Kyoo.WebApp .
WORKDIR /webapp/Front
RUN npm install
RUN npm run build -- --prod
@ -19,6 +20,6 @@ RUN apt-get update && apt-get install -y libavutil-dev libavcodec-dev libavforma
EXPOSE 5000
COPY --from=builder /opt/kyoo /usr/lib/kyoo
COPY --from=transcoder /transcoder/libtranscoder.so /usr/lib/kyoo
COPY --from=webapp /webapp/dist/* /usr/lib/kyoo/wwwroot/
COPY --from=webapp /webapp/Front/dist/* /usr/lib/kyoo/wwwroot/
CMD ["/usr/lib/kyoo/Kyoo", "/var/lib/kyoo"]

View File

@ -1,9 +1,10 @@
using System;
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using JetBrains.Annotations;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A class to ease configuration management. This work WITH Microsoft's package, you can still use IOptions patterns
@ -26,6 +27,15 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="path">The root path of the editable configuration. It should not be a nested type.</param>
void AddUntyped(string path);
/// <summary>
/// An helper method of <see cref="AddTyped{T}"/> and <see cref="AddUntyped"/>.
/// This register a typed value if <paramref name="type"/> is not <c>null</c> and registers an untyped type
/// if <paramref name="type"/> is <c>null</c>.
/// </summary>
/// <param name="path">The root path of the editable configuration. It should not be a nested type.</param>
/// <param name="type">The type of the configuration or null.</param>
void Register(string path, [CanBeNull] Type type);
/// <summary>
/// Get the value of a setting using it's path.

View File

@ -2,25 +2,11 @@ using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Abstractions.Models;
using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A class wrapping a value that will be set after the completion of the task it is related to.
/// </summary>
/// <remarks>
/// This class replace the use of an out parameter on a task since tasks and out can't be combined.
/// </remarks>
/// <typeparam name="T">The type of the value</typeparam>
public class AsyncRef<T>
{
/// <summary>
/// The value that will be set before the completion of the task.
/// </summary>
public T Value { get; set; }
}
/// <summary>
/// A service to abstract the file system to allow custom file systems (like distant file systems or external providers)
/// </summary>

View File

@ -1,8 +1,8 @@
using System.Threading.Tasks;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// An interface to identify episodes, shows and metadata based on the episode file.

View File

@ -4,10 +4,10 @@ using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// An interface to interract with the database. Every repository is mapped through here.

View File

@ -1,9 +1,9 @@
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// An interface to automatically retrieve metadata from external providers.

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using Autofac;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A common interface used to discord plugins
/// </summary>
/// <remarks>
/// You can inject services in the IPlugin constructor.
/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.
/// </remarks>
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public interface IPlugin
{
/// <summary>
/// A slug to identify this plugin in queries.
/// </summary>
string Slug { get; }
/// <summary>
/// The name of the plugin
/// </summary>
string Name { get; }
/// <summary>
/// The description of this plugin. This will be displayed on the "installed plugins" page.
/// </summary>
string Description { get; }
/// <summary>
/// <c>true</c> if the plugin should be enabled, <c>false</c> otherwise.
/// If a plugin is not enabled, no configure method will be called.
/// This allow one to enable a plugin if a specific configuration value is set or if the environment contains
/// the right settings.
/// </summary>
/// <remarks>
/// By default, a plugin is always enabled. This method can be overriden to change this behavior.
/// </remarks>
virtual bool Enabled => true;
/// <summary>
/// A list of types that will be available via the IOptions interfaces and will be listed inside
/// an IConfiguration.
///
/// If a field should be loosely typed, <see cref="Dictionary{TKey,TValue}"/> or <c>null</c>
/// can be specified.
/// WARNING: null means an unmanaged type that won't be editable. This can be used
/// for external libraries or variable arguments.
/// </summary>
/// <remarks>
/// All use of the configuration must be specified here and not registered elsewhere, if a type is registered
/// elsewhere the configuration won't be editable via the <see cref="IConfigurationManager"/> and all values
/// will be discarded on edit.
/// </remarks>
Dictionary<string, Type> Configuration { get; }
/// <summary>
/// An optional configuration step to allow a plugin to change asp net configurations.
/// </summary>
/// <seealso cref="SA"/>
virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
/// <summary>
/// A configure method that will be run on plugin's startup.
/// </summary>
/// <param name="builder">The autofac service container to register services.</param>
void Configure(ContainerBuilder builder)
{
// Skipped
}
/// <summary>
/// A configure method that will be run on plugin's startup.
/// This is available for libraries that build upon a <see cref="IServiceCollection"/>, for more precise
/// configuration use <see cref="Configure(Autofac.ContainerBuilder)"/>.
/// </summary>
/// <param name="services">A service container to register new services.</param>
void Configure(IServiceCollection services)
{
// Skipped
}
/// <summary>
/// An optional function to execute and initialize your plugin.
/// It can be used to initialize a database connection, fill initial data or anything.
/// </summary>
/// <param name="provider">A service provider to request services</param>
void Initialize(IServiceProvider provider)
{
// 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

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using Autofac;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A manager to load plugins and retrieve information from them.
@ -41,23 +39,14 @@ namespace Kyoo.Controllers
/// You should not try to put plugins from the plugins directory here as they will get automatically loaded.
/// </param>
public void LoadPlugins(ICollection<IPlugin> plugins);
/// <summary>
/// Configure container adding or removing services as the plugins wants.
/// </summary>
/// <param name="builder">The container to populate</param>
void ConfigureContainer(ContainerBuilder builder);
/// <summary>
/// Configure services via the microsoft way. This allow libraries to add their services.
/// Load plugins and their dependencies from the plugin directory.
/// </summary>
/// <param name="services">The service collection to populate</param>
public void ConfigureServices(IServiceCollection services);
/// <summary>
/// Configure an asp net application applying plugins policies.
/// </summary>
/// <param name="app">The asp net application to configure</param>
public void ConfigureAspnet(IApplicationBuilder app);
/// <param name="plugins">
/// An initial plugin list to use.
/// You should not try to put plugins from the plugins directory here as they will get automatically loaded.
/// </param>
public void LoadPlugins(params Type[] plugins);
}
}

View File

@ -4,10 +4,10 @@ using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// Information about the pagination. How many items should be displayed and where to start.
@ -106,7 +106,7 @@ namespace Kyoo.Controllers
}
/// <summary>
/// A base class for repositories. Every service implementing this will be handled by the <see cref="LibraryManager"/>.
/// A base class for repositories. Every service implementing this will be handled by the <see cref="ILibraryManager"/>.
/// </summary>
public interface IBaseRepository
{
@ -309,20 +309,20 @@ namespace Kyoo.Controllers
Task<Season> Get(string showSlug, int seasonNumber);
/// <summary>
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns>
Task<Season> GetOrDefault(int showID, int seasonNumber);
/// <summary>
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns>
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns>
Task<Season> GetOrDefault(int showID, int seasonNumber);
/// <summary>
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns>
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
}
/// <summary>

View File

@ -4,11 +4,10 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models;
using Kyoo.Models.Attributes;
using Kyoo.Models.Exceptions;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A single task parameter. This struct contains metadata to display and utility functions to get them in the task.
@ -55,7 +54,7 @@ namespace Kyoo.Controllers
/// <returns>A new task parameter.</returns>
public static TaskParameter Create<T>(string name, string description)
{
return new()
return new TaskParameter
{
Name = name,
Description = description,
@ -72,7 +71,7 @@ namespace Kyoo.Controllers
/// <returns>A new task parameter.</returns>
public static TaskParameter CreateRequired<T>(string name, string description)
{
return new()
return new TaskParameter
{
Name = name,
Description = description,
@ -182,11 +181,6 @@ namespace Kyoo.Controllers
/// <param name="cancellationToken">A token to request the task's cancellation.
/// If this task is not cancelled quickly, it might be killed by the runner.
/// </param>
/// <remarks>
/// Your task can have any service as a public field and use the <see cref="InjectedAttribute"/>,
/// they will be set to an available service from the service container before calling this method.
/// They also will be removed after this method return (or throw) to prevent dangling services.
/// </remarks>
/// <exception cref="TaskFailedException">
/// An exception meaning that the task has failed for handled reasons like invalid arguments,
/// invalid environment, missing plugins or failures not related to a default in the code.

View File

@ -2,10 +2,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using Kyoo.Common.Models.Attributes;
using Kyoo.Models.Exceptions;
using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A service to handle long running tasks.

View File

@ -1,8 +1,8 @@
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// Download images and retrieve the path of those images for a resource.

View File

@ -1,7 +1,7 @@
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using System.Threading.Tasks;
namespace Kyoo.Controllers
namespace Kyoo.Abstractions.Controllers
{
public interface ITranscoder
{

View File

@ -0,0 +1,221 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Controllers
{
/// <summary>
/// A list of constant priorities used for <see cref="IStartupAction"/>'s <see cref="IStartupAction.Priority"/>.
/// It also contains helper methods for creating new <see cref="StartupAction"/>.
/// </summary>
public static class SA
{
public const int Before = 5000;
public const int Routing = 4000;
public const int StaticFiles = 3000;
public const int Authentication = 2000;
public const int Authorization = 1000;
public const int Endpoint = 0;
public const int After = -1000;
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction New(Action action, int priority)
=> new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T> New<T>(Action<T> action, int priority)
=> new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2> New<T, T2>(Action<T, T2> action, int priority)
=> new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <typeparam name="T3">A third dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2, T3> New<T, T2, T3>(Action<T, T2, T3> action, int priority)
=> new(action, priority);
}
/// <summary>
/// An action executed on kyoo's startup to initialize the asp-net container.
/// </summary>
/// <remarks>
/// This is the base interface, see <see cref="StartupAction"/> for a simpler use of this.
/// </remarks>
public interface IStartupAction
{
/// <summary>
/// The priority of this action. The actions will be executed on descending priority order.
/// If two actions have the same priority, their order is undefined.
/// </summary>
int Priority { get; }
/// <summary>
/// Run this action to configure the container, a service provider containing all services can be used.
/// </summary>
/// <param name="provider">The service provider containing all services can be used.</param>
void Run(IServiceProvider provider);
}
/// <summary>
/// A <see cref="IStartupAction"/> with no dependencies.
/// </summary>
public class StartupAction : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke();
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with one dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
public class StartupAction<T> : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(provider.GetRequiredService<T>());
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with two dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
/// <typeparam name="T2">The second dependency to use.</typeparam>
public class StartupAction<T, T2> : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T, T2> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T, T2> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(
provider.GetRequiredService<T>(),
provider.GetRequiredService<T2>()
);
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with three dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
/// <typeparam name="T2">The second dependency to use.</typeparam>
/// <typeparam name="T3">The third dependency to use.</typeparam>
public class StartupAction<T, T2, T3> : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T, T2, T3> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2, T3}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T, T2, T3> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(
provider.GetRequiredService<T>(),
provider.GetRequiredService<T2>(),
provider.GetRequiredService<T3>()
);
}
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Title>Kyoo.Common</Title>
<Title>Kyoo.Abstractions</Title>
<Authors>Zoe Roux</Authors>
<Description>Base package to create plugins for Kyoo.</Description>
<PackageProjectUrl>https://github.com/AnonymusRaccoon/Kyoo</PackageProjectUrl>
@ -12,10 +12,11 @@
<Company>SDG</Company>
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageVersion>1.0.25</PackageVersion>
<PackageVersion>1.0.0</PackageVersion>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<LangVersion>default</LangVersion>
<RootNamespace>Kyoo.Abstractions</RootNamespace>
</PropertyGroup>
<ItemGroup>

View File

@ -0,0 +1,17 @@
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A class wrapping a value that will be set after the completion of the task it is related to.
/// </summary>
/// <remarks>
/// This class replace the use of an out parameter on a task since tasks and out can't be combined.
/// </remarks>
/// <typeparam name="T">The type of the value</typeparam>
public class AsyncRef<T>
{
/// <summary>
/// The value that will be set before the completion of the task.
/// </summary>
public T Value { get; set; }
}
}

View File

@ -1,6 +1,6 @@
using System;
namespace Kyoo.Models.Attributes
namespace Kyoo.Abstractions.Models.Attributes
{
/// <summary>
/// An attribute to inform that the property is computed automatically and can't be assigned manually.

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Common.Models.Attributes
namespace Kyoo.Abstractions.Models.Attributes
{
/// <summary>
/// An attribute to inform how a <see cref="IFileSystem"/> works.

View File

@ -1,6 +1,6 @@
using System;
namespace Kyoo.Models.Attributes
namespace Kyoo.Abstractions.Models.Attributes
{
/// <summary>
/// Specify that a property can't be merged.

View File

@ -2,7 +2,7 @@ using System;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Models.Permissions
namespace Kyoo.Abstractions.Models.Permissions
{
/// <summary>
/// The kind of permission needed.

View File

@ -1,7 +1,7 @@
using System;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Models.Attributes
namespace Kyoo.Abstractions.Models.Attributes
{
/// <summary>
/// The targeted relation can be edited via calls to the repository's <see cref="IRepository{T}.Edit"/> method.

View File

@ -1,6 +1,6 @@
using System;
namespace Kyoo.Models.Attributes
namespace Kyoo.Abstractions.Models.Attributes
{
/// <summary>
/// Remove an property from the serialization pipeline. It will simply be skipped.

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Common.Models.Attributes
namespace Kyoo.Abstractions.Models.Attributes
{
/// <summary>
/// An attribute to inform how a <see cref="IFileSystem"/> works.

View File

@ -1,4 +1,4 @@
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A chapter to split an episode in multiple parts.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Reflection;
using JetBrains.Annotations;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A class given information about a strongly typed configuration.

View File

@ -1,7 +1,7 @@
using System;
using System.Runtime.Serialization;
namespace Kyoo.Models.Exceptions
namespace Kyoo.Abstractions.Models.Exceptions
{
/// <summary>
/// An exception raised when an item already exists in the database.

View File

@ -1,8 +1,8 @@
using System;
using System.Runtime.Serialization;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Models.Exceptions
namespace Kyoo.Abstractions.Models.Exceptions
{
/// <summary>
/// An exception raised when an <see cref="IIdentifier"/> failed.

View File

@ -1,7 +1,7 @@
using System;
using System.Runtime.Serialization;
namespace Kyoo.Models.Exceptions
namespace Kyoo.Abstractions.Models.Exceptions
{
/// <summary>
/// An exception raised when an item could not be found.

View File

@ -1,8 +1,8 @@
using System;
using System.Runtime.Serialization;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Models.Exceptions
namespace Kyoo.Abstractions.Models.Exceptions
{
/// <summary>
/// An exception raised when an <see cref="ITask"/> failed.

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// The type of item, ether a show, a movie or a collection.

View File

@ -1,8 +1,8 @@
using System;
using System.Linq.Expressions;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// ID and link of an item on an external provider.

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A page of resource that contains information about the pagination of resources.

View File

@ -1,4 +1,4 @@
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A role a person played for a show. It can be an actor, musician, voice actor, director, writer...

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A class representing collections of <see cref="Show"/>.

View File

@ -2,10 +2,10 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A class to represent a single show's episode.

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A genre that allow one to specify categories for shows.

View File

@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// An interface applied to resources containing external metadata.

View File

@ -1,6 +1,6 @@
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// An interface to represent a resource that can be retrieved from the database.

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// An interface representing items that contains images (like posters, thumbnails, logo, banners...)

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A library containing <see cref="Show"/> and <see cref="Collection"/>.

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// An actor, voice actor, writer, animator, somebody who worked on a <see cref="Show"/>.

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// This class contains metadata about <see cref="IMetadataProvider"/>.

View File

@ -2,10 +2,10 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A season of a <see cref="Show"/>.

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A series or a movie.

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A studio that make shows.

View File

@ -3,9 +3,9 @@ using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// The list of available stream types.
@ -60,9 +60,9 @@ namespace Kyoo.Models
}
/// <summary>
/// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
/// </summary>
[SerializeIgnore] public string EpisodeSlug { private get; set; }
/// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
/// </summary>
[SerializeIgnore] public string EpisodeSlug { private get; set; }
/// <summary>
/// The title of the stream.

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A single user of the app.

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// Results of a search request.

View File

@ -3,11 +3,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes;
using PathIO = System.IO.Path;
namespace Kyoo.Models
namespace Kyoo.Abstractions.Models
{
/// <summary>
/// A watch item give information useful for playback.

View File

@ -1,9 +1,9 @@
using Autofac;
using Autofac.Builder;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
using Microsoft.Extensions.Configuration;
namespace Kyoo
namespace Kyoo.Abstractions
{
/// <summary>
/// A static class with helper functions to setup external modules

View File

@ -5,8 +5,8 @@ using System.ComponentModel;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Kyoo.Models;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo
{

View File

@ -21,17 +21,17 @@ namespace Kyoo
/// <exception cref="TaskCanceledException"></exception>
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
public static Task<T> Then<T>(this Task<T> task, Action<T> then)
{
return task.ContinueWith(x =>
{
if (x.IsFaulted)
x.Exception!.InnerException!.ReThrow();
if (x.IsCanceled)
throw new TaskCanceledException();
then(x.Result);
return x.Result;
}, TaskContinuationOptions.ExecuteSynchronously);
}
{
return task.ContinueWith(x =>
{
if (x.IsFaulted)
x.Exception!.InnerException!.ReThrow();
if (x.IsCanceled)
throw new TaskCanceledException();
then(x.Result);
return x.Result;
}, TaskContinuationOptions.ExecuteSynchronously);
}
/// <summary>
/// Map the result of a task to another result.
@ -42,28 +42,28 @@ namespace Kyoo
/// <typeparam name="TResult">The resulting task after the mapping method</typeparam>
/// <returns>A task wrapping the initial task and mapping the initial result.</returns>
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map)
{
return task.ContinueWith(x =>
{
if (x.IsFaulted)
x.Exception!.InnerException!.ReThrow();
if (x.IsCanceled)
throw new TaskCanceledException();
return map(x.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}
public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map)
{
return task.ContinueWith(x =>
{
if (x.IsFaulted)
x.Exception!.InnerException!.ReThrow();
if (x.IsCanceled)
throw new TaskCanceledException();
return map(x.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
}
/// <summary>
/// A method to return the a default value from a task if the initial task is null.
/// </summary>
/// <param name="value">The initial task</param>
/// <typeparam name="T">The type that the task will return</typeparam>
/// <returns>A non-null task.</returns>
[NotNull]
public static Task<T> DefaultIfNull<T>([CanBeNull] Task<T> value)
{
return value ?? Task.FromResult<T>(default);
}
/// <summary>
/// A method to return the a default value from a task if the initial task is null.
/// </summary>
/// <param name="value">The initial task</param>
/// <typeparam name="T">The type that the task will return</typeparam>
/// <returns>A non-null task.</returns>
[NotNull]
public static Task<T> DefaultIfNull<T>([CanBeNull] Task<T> value)
{
return value ?? Task.FromResult<T>(default);
}
}
}

View File

@ -155,7 +155,8 @@ namespace Kyoo
IEnumerable<Type> types = genericType.IsInterface
? type.GetInterfaces()
: type.GetInheritanceTree();
return types.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
return types.Prepend(type)
.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
}
/// <summary>
@ -179,7 +180,8 @@ namespace Kyoo
IEnumerable<Type> types = genericType.IsInterface
? type.GetInterfaces()
: type.GetInheritanceTree();
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
return types.Prepend(type)
.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
}
/// <summary>

View File

@ -3,14 +3,15 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Autofac;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Authentication.Models;
using Kyoo.Authentication.Views;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@ -37,58 +38,61 @@ namespace Kyoo.Authentication
/// <inheritdoc />
public string Description => "Enable OpenID authentication for Kyoo.";
/// <inheritdoc />
public ICollection<Type> Provides => ArraySegment<Type>.Empty;
/// <inheritdoc />
public ICollection<ConditionalProvide> ConditionalProvides => ArraySegment<ConditionalProvide>.Empty;
/// <inheritdoc />
public ICollection<Type> Requires => new []
public Dictionary<string, Type> Configuration => new()
{
typeof(IUserRepository)
{ AuthenticationOption.Path, typeof(AuthenticationOption) },
{ PermissionOption.Path, typeof(PermissionOption) },
{ CertificateOption.Path, typeof(CertificateOption) }
};
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// A logger factory to allow IdentityServer to log things.
/// The logger used to allow IdentityServer to log things.
/// </summary>
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger<DefaultCorsPolicyService> _logger;
/// <summary>
/// The environment information to check if the app runs in debug mode
/// </summary>
private readonly IWebHostEnvironment _environment;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
/// Create a new authentication module instance and use the given configuration and environment.
/// </summary>
/// <param name="configuration">The configuration to use</param>
/// <param name="loggerFactory">The logger factory to allow IdentityServer to log things</param>
/// <param name="logger">The logger used to allow IdentityServer to log things</param>
/// <param name="environment">The environment information to check if the app runs in debug mode</param>
public AuthenticationModule(IConfiguration configuration,
ILoggerFactory loggerFactory,
ILogger<DefaultCorsPolicyService> logger,
IWebHostEnvironment environment)
{
_configuration = configuration;
_loggerFactory = loggerFactory;
_logger = logger;
_environment = environment;
}
/// <inheritdoc />
public void Configure(IServiceCollection services, ICollection<Type> availableTypes)
public void Configure(ContainerBuilder builder)
{
builder.RegisterType<PermissionValidatorFactory>().As<IPermissionValidator>().SingleInstance();
DefaultCorsPolicyService cors = new(_logger)
{
AllowedOrigins = { new Uri(_configuration.GetPublicUrl()).GetLeftPart(UriPartial.Authority) }
};
builder.RegisterInstance(cors).As<ICorsPolicyService>().SingleInstance();
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
{
string publicUrl = _configuration.GetPublicUrl();
@ -100,10 +104,6 @@ namespace Kyoo.Authentication
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
// TODO Check if tokens should be stored.
services.Configure<PermissionOption>(_configuration.GetSection(PermissionOption.Path));
services.Configure<CertificateOption>(_configuration.GetSection(CertificateOption.Path));
services.Configure<AuthenticationOption>(_configuration.GetSection(AuthenticationOption.Path));
List<Client> clients = new();
_configuration.GetSection("authentication:clients").Bind(clients);
@ -131,47 +131,46 @@ namespace Kyoo.Authentication
options.Audience = "kyoo";
options.RequireHttpsMetadata = false;
});
services.AddSingleton<IPermissionValidator, PermissionValidatorFactory>();
DefaultCorsPolicyService cors = new(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
{
AllowedOrigins = {new Uri(publicUrl).GetLeftPart(UriPartial.Authority)}
};
services.AddSingleton<ICorsPolicyService>(cors);
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
public IEnumerable<IStartupAction> ConfigureSteps => new IStartupAction[]
{
ConfigurationManager.AddTyped<AuthenticationOption>(AuthenticationOption.Path);
app.UseCookiePolicy(new CookiePolicyOptions
SA.New<IApplicationBuilder>(app =>
{
MinimumSameSitePolicy = SameSiteMode.Strict
});
app.UseAuthentication();
app.Use((ctx, next) =>
PhysicalFileProvider provider = new(Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
"login"));
app.UseDefaultFiles(new DefaultFilesOptions
{
RequestPath = new PathString("/login"),
FileProvider = provider,
RedirectToAppendTrailingSlash = true
});
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = new PathString("/login"),
FileProvider = provider
});
}, SA.StaticFiles),
SA.New<IApplicationBuilder>(app =>
{
ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl());
return next();
});
app.UseIdentityServer();
app.UseAuthorization();
PhysicalFileProvider provider = new(Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
"login"));
app.UseDefaultFiles(new DefaultFilesOptions
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict
});
app.UseAuthentication();
}, SA.Authentication),
SA.New<IApplicationBuilder>(app =>
{
RequestPath = new PathString("/login"),
FileProvider = provider,
RedirectToAppendTrailingSlash = true
});
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = new PathString("/login"),
FileProvider = provider
});
}
app.Use((ctx, next) =>
{
ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl());
return next();
});
app.UseIdentityServer();
}, SA.Endpoint),
SA.New<IApplicationBuilder>(app => app.UseAuthorization(), SA.Authorization)
};
}
}

View File

@ -34,7 +34,7 @@ namespace Kyoo.Authentication
X509Certificate2 certificate = GetCertificate(options);
builder.AddSigningCredential(certificate);
if (certificate.NotAfter.AddDays(7) <= DateTime.UtcNow)
if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow)
{
Console.WriteLine("Signin certificate will expire soon, renewing it.");
if (File.Exists(options.OldFile))
@ -67,7 +67,7 @@ namespace Kyoo.Authentication
/// <returns>The loaded certificate</returns>
private static X509Certificate2 GetExistingCredential(string file, string password)
{
return new(file, password,
return new X509Certificate2(file, password,
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable

View File

@ -2,8 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Abstractions.Models.Permissions;
using Kyoo.Authentication.Models;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;

View File

@ -3,7 +3,7 @@ using System.Linq;
using System.Security.Claims;
using IdentityModel;
using IdentityServer4;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
namespace Kyoo.Authentication
{

View File

@ -2,18 +2,12 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputPath>../Kyoo/bin/$(Configuration)/$(TargetFramework)/plugins/authentication</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<GenerateDependencyFile>false</GenerateDependencyFile>
<GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
<Company>SDG</Company>
<Authors>Zoe Roux</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<LangVersion>default</LangVersion>
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
</PropertyGroup>
<ItemGroup>
@ -23,28 +17,14 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
<PrivateAssets>all</PrivateAssets>
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference>
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<LoginFiles Include="$(LoginRoot)**" Visible="false" />
<None Remove="$(LoginRoot)**;" />
<Content Include="$(LoginRoot)**">
<Link>login/%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="Publish login files" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Include="@(LoginFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>login/%(LoginFiles.RecursiveDir)%(LoginFiles.Filename)%(LoginFiles.Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
<Target Name="Prepare static files" AfterTargets="Build" Condition="$(Configuration) == 'Debug'">
<Copy SourceFiles="@(LoginFiles)" DestinationFolder="$(OutputPath)/login/%(RecursiveDir)" />
</Target>
</Project>

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
namespace Kyoo.Authentication.Models.DTO
{
@ -34,7 +34,7 @@ namespace Kyoo.Authentication.Models.DTO
/// <returns></returns>
public User ToUser()
{
return new()
return new User
{
Slug = Utility.ToSlug(Username),
Username = Username,

View File

@ -8,11 +8,11 @@ using System.Threading.Tasks;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Authentication.Models;
using Kyoo.Authentication.Models.DTO;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;

View File

@ -1,247 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
/// <summary>
/// A common interface used to discord plugins
/// </summary>
/// <remarks>
/// You can inject services in the IPlugin constructor.
/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.
/// </remarks>
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public interface IPlugin
{
/// <summary>
/// A slug to identify this plugin in queries.
/// </summary>
string Slug { get; }
/// <summary>
/// The name of the plugin
/// </summary>
string Name { get; }
/// <summary>
/// The description of this plugin. This will be displayed on the "installed plugins" page.
/// </summary>
string Description { get; }
/// <summary>
/// A list of services that are provided by this service. This allow other plugins to declare dependencies
/// </summary>
/// <remarks>
/// You should put the type's interface that will be register in configure.
/// </remarks>
ICollection<Type> Provides { get; }
/// <summary>
/// A list of types that will be provided only if a condition is met. The condition can be an arbitrary method or
/// a condition based on other type availability. For more information, see <see cref="ConditionalProvides"/>.
/// </summary>
ICollection<ConditionalProvide> ConditionalProvides { get; }
/// <summary>
/// A list of services that are required by this plugin.
/// You can put services that you provide conditionally here if you want.
/// Kyoo will warn the user that this plugin can't be loaded if a required service is not found.
/// </summary>
/// <remarks>
/// Put here the most complete type that are needed for your plugin to work. If you need a LibraryManager,
/// put typeof(ILibraryManager).
/// </remarks>
ICollection<Type> Requires { get; }
/// <summary>
/// A configure method that will be run on plugin's startup.
/// </summary>
/// <param name="builder">The autofac service container to register services.</param>
void Configure(ContainerBuilder builder)
{
// Skipped
}
/// <summary>
/// A configure method that will be run on plugin's startup.
/// </summary>
/// <param name="services">A service container to register new services.</param>
/// <param name="availableTypes">The list of types that are available for this instance. This can be used
/// for conditional type. See <see cref="ProviderCondition.Has(System.Type,System.Collections.Generic.ICollection{System.Type})"/>
/// or <see cref="ProviderCondition.Has(System.Collections.Generic.ICollection{System.Type},System.Collections.Generic.ICollection{System.Type})"/>>
/// You can't simply check on the service collection because some dependencies might be registered after your plugin.
/// </param>
void Configure(IServiceCollection services, ICollection<Type> availableTypes)
{
// Skipped
}
/// <summary>
/// An optional configuration step to allow a plugin to change asp net configurations.
/// WARNING: This is only called on Kyoo's startup so you must restart the app to apply this changes.
/// </summary>
/// <param name="app">
/// The Asp.Net application builder. On most case it is not needed but you can use it to
/// add asp net functionalities.
/// </param>
void ConfigureAspNet(IApplicationBuilder app)
{
// Skipped
}
/// <summary>
/// An optional function to execute and initialize your plugin.
/// It can be used to initialize a database connection, fill initial data or anything.
/// </summary>
/// <param name="provider">A service provider to request services</param>
void Initialize(IServiceProvider provider)
{
// Skipped
}
}
/// <summary>
/// A type that will only be provided if a special condition is met. To check that your condition is met,
/// you can check the <see cref="ProviderCondition"/> class.
/// </summary>
public class ConditionalProvide : Tuple<Type, ProviderCondition>
{
/// <summary>
/// Get the type that may be provided
/// </summary>
public Type Type => Item1;
/// <summary>
/// Get the condition.
/// </summary>
public ProviderCondition Condition => Item2;
/// <summary>
/// Create a <see cref="ConditionalProvide"/> from a type and a condition.
/// </summary>
/// <param name="type">The type to provide</param>
/// <param name="condition">The condition</param>
public ConditionalProvide(Type type, ProviderCondition condition)
: base(type, condition)
{ }
/// <summary>
/// Create a <see cref="ConditionalProvide"/> from a tuple of (Type, ProviderCondition).
/// </summary>
/// <param name="tuple">The tuple to convert</param>
public ConditionalProvide((Type type, ProviderCondition condition) tuple)
: base(tuple.type, tuple.condition)
{ }
/// <summary>
/// Implicitly convert a tuple to a <see cref="ConditionalProvide"/>.
/// </summary>
/// <param name="tuple">The tuple to convert</param>
/// <returns>A new <see cref="ConditionalProvide"/> based on the given tuple.</returns>
public static implicit operator ConditionalProvide((Type, Type) tuple) => new (tuple);
}
/// <summary>
/// A condition for a conditional type.
/// </summary>
public class ProviderCondition
{
/// <summary>
/// The condition as a method. If true is returned, the type will be provided.
/// </summary>
public Func<bool> Condition { get; } = () => true;
/// <summary>
/// The list of types that this method needs.
/// </summary>
public ICollection<Type> Needed { get; } = ArraySegment<Type>.Empty;
/// <summary>
/// Create a new <see cref="ProviderCondition"/> from a raw function.
/// </summary>
/// <param name="condition">The predicate that will be used as condition</param>
public ProviderCondition(Func<bool> condition)
{
Condition = condition;
}
/// <summary>
/// Create a new <see cref="ProviderCondition"/> from a type. This allow you to inform that a type will
/// only be available if a dependency is met.
/// </summary>
/// <param name="needed">The type that you need</param>
public ProviderCondition(Type needed)
{
Needed = new[] {needed};
}
/// <summary>
/// Create a new <see cref="ProviderCondition"/> from a list of type. This allow you to inform that a type will
/// only be available if a list of dependencies are met.
/// </summary>
/// <param name="needed">The types that you need</param>
public ProviderCondition(ICollection<Type> needed)
{
Needed = needed;
}
/// <summary>
/// Create a new <see cref="ProviderCondition"/> with a list of types as dependencies and a predicate
/// for arbitrary conditions.
/// </summary>
/// <param name="needed">The list of dependencies</param>
/// <param name="condition">An arbitrary condition</param>
public ProviderCondition(ICollection<Type> needed, Func<bool> condition)
{
Needed = needed;
Condition = condition;
}
/// <summary>
/// Implicitly convert a type to a <see cref="ProviderCondition"/>.
/// </summary>
/// <param name="type">The type dependency</param>
/// <returns>A <see cref="ProviderCondition"/> that will return true if the given type is available.</returns>
public static implicit operator ProviderCondition(Type type) => new(type);
/// <summary>
/// Implicitly convert a list of type to a <see cref="ProviderCondition"/>.
/// </summary>
/// <param name="types">The list of type dependencies</param>
/// <returns>A <see cref="ProviderCondition"/> that will return true if the given types are available.</returns>
public static implicit operator ProviderCondition(Type[] types) => new(types);
/// <inheritdoc cref="op_Implicit(System.Type[])"/>
public static implicit operator ProviderCondition(List<Type> types) => new(types);
/// <summary>
/// Check if a type is available.
/// </summary>
/// <param name="needed">The type to check</param>
/// <param name="available">The list of types</param>
/// <returns>True if the dependency is met, false otherwise</returns>
public static bool Has(Type needed, ICollection<Type> available)
{
return available.Contains(needed);
}
/// <summary>
/// Check if a list of type are available.
/// </summary>
/// <param name="needed">The list of types to check</param>
/// <param name="available">The list of types</param>
/// <returns>True if the dependencies are met, false otherwise</returns>
public static bool Has(ICollection<Type> needed, ICollection<Type> available)
{
return needed.All(x => Has(x, available));
}
}
}

View File

@ -1,17 +0,0 @@
using System;
using JetBrains.Annotations;
using Kyoo.Controllers;
namespace Kyoo.Models.Attributes
{
/// <summary>
/// An attribute to inform that the service will be injected automatically by a service provider.
/// </summary>
/// <remarks>
/// It should only be used on <see cref="IPlugin"/> and it will be injected before
/// calling <see cref="IPlugin.ConfigureAspNet"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Property)]
[MeansImplicitUse(ImplicitUseKindFlags.Assign)]
public class InjectedAttribute : Attribute { }
}

View File

@ -5,13 +5,13 @@ using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Kyoo
namespace Kyoo.Database
{
/// <summary>
/// The database handle used for all local repositories.

View File

@ -11,16 +11,26 @@ namespace Kyoo
/// <summary>
/// Get a connection string from the Configuration's section "Database"
/// </summary>
/// <param name="config">The IConfiguration instance to load.</param>
/// <param name="config">The IConfiguration instance to use.</param>
/// <param name="database">The database's name.</param>
/// <returns>A parsed connection string</returns>
public static string GetDatabaseConnection(this IConfiguration config, string database)
{
DbConnectionStringBuilder builder = new();
IConfigurationSection section = config.GetSection("Database").GetSection(database);
IConfigurationSection section = config.GetSection("database:configurations").GetSection(database);
foreach (IConfigurationSection child in section.GetChildren())
builder[child.Key] = child.Value;
return builder.ConnectionString;
}
/// <summary>
/// Get the name of the selected database.
/// </summary>
/// <param name="config">The IConfiguration instance to use.</param>
/// <returns>The name of the selected database.</returns>
public static string GetSelectedDatabase(this IConfiguration config)
{
return config.GetValue<string>("database:enabled");
}
}
}

View File

@ -2,9 +2,8 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>Kyoo.CommonApi</AssemblyName>
<RootNamespace>Kyoo.CommonApi</RootNamespace>
<PackageId>Kyoo.CommonApi</PackageId>
<AssemblyName>Kyoo.Database</AssemblyName>
<RootNamespace>Kyoo.Database</RootNamespace>
<Authors>Zoe Roux</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<OutputType>Library</OutputType>
@ -19,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Kyoo.Common\Kyoo.Common.csproj" />
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -18,9 +18,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj">
</ProjectReference>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
</ProjectReference>
<ProjectReference Include="..\Kyoo.Database\Kyoo.Database.csproj" />
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;

View File

@ -1,7 +1,7 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@ -1,7 +1,7 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;

View File

@ -3,7 +3,8 @@ using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using EFCore.NamingConventions.Internal;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using Kyoo.Database;
using Microsoft.EntityFrameworkCore;
using Npgsql;

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
using Kyoo.Database;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@ -23,19 +24,12 @@ namespace Kyoo.Postgresql
/// <inheritdoc />
public string Description => "A database context for postgresql.";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <inheritdoc />
public ICollection<Type> Provides => new[]
{
typeof(DatabaseContext)
};
/// <inheritdoc />
public ICollection<ConditionalProvide> ConditionalProvides => ArraySegment<ConditionalProvide>.Empty;
/// <inheritdoc />
public ICollection<Type> Requires => ArraySegment<Type>.Empty;
public bool Enabled => _configuration.GetSelectedDatabase() == "postgres";
/// <summary>
/// The configuration to use. The database connection string is pulled from it.
@ -59,7 +53,7 @@ namespace Kyoo.Postgresql
}
/// <inheritdoc />
public void Configure(IServiceCollection services, ICollection<Type> availableTypes)
public void Configure(IServiceCollection services)
{
services.AddDbContext<DatabaseContext, PostgresContext>(x =>
{

View File

@ -18,9 +18,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj">
</ProjectReference>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
</ProjectReference>
<ProjectReference Include="..\Kyoo.Database\Kyoo.Database.csproj" />
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -2,7 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using Kyoo.Database;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Kyoo.Controllers;
using Kyoo.Abstractions.Controllers;
using Kyoo.Database;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@ -22,20 +23,14 @@ namespace Kyoo.SqLite
/// <inheritdoc />
public string Description => "A database context for sqlite.";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <inheritdoc />
public bool Enabled => _configuration.GetSelectedDatabase() == "sqlite";
/// <inheritdoc />
public ICollection<Type> Provides => new[]
{
typeof(DatabaseContext)
};
/// <inheritdoc />
public ICollection<ConditionalProvide> ConditionalProvides => ArraySegment<ConditionalProvide>.Empty;
/// <inheritdoc />
public ICollection<Type> Requires => ArraySegment<Type>.Empty;
/// <summary>
/// The configuration to use. The database connection string is pulled from it.
/// </summary>
@ -59,7 +54,7 @@ namespace Kyoo.SqLite
/// <inheritdoc />
public void Configure(IServiceCollection services, ICollection<Type> availableTypes)
public void Configure(IServiceCollection services)
{
services.AddDbContext<DatabaseContext, SqLiteContext>(x =>
{

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.Search;
namespace Kyoo.TheMovieDb

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.TvShows;
namespace Kyoo.TheMovieDb

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.Movies;
using TMDbLib.Objects.Search;

View File

@ -1,9 +1,9 @@
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.General;
using TMDbLib.Objects.People;
using TMDbLib.Objects.Search;
using Images = Kyoo.Models.Images;
using Images = Kyoo.Abstractions.Models.Images;
using TvCast = TMDbLib.Objects.TvShows.Cast;
using MovieCast = TMDbLib.Objects.Movies.Cast;
@ -15,11 +15,11 @@ namespace Kyoo.TheMovieDb
public static partial class Convertors
{
/// <summary>
/// Convert a <see cref="MovieCast"/> to a <see cref="PeopleRole"/>.
/// Convert a <see cref="MovieCast"/> to a <see cref="Abstractions.Models.PeopleRole"/>.
/// </summary>
/// <param name="cast">An internal TheMovieDB cast.</param>
/// <param name="provider">The provider that represent TheMovieDB inside Kyoo.</param>
/// <returns>A <see cref="PeopleRole"/> representing the movie cast.</returns>
/// <returns>A <see cref="Abstractions.Models.PeopleRole"/> representing the movie cast.</returns>
public static PeopleRole ToPeople(this MovieCast cast, Provider provider)
{
return new PeopleRole

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.TvShows;
namespace Kyoo.TheMovieDb

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.Search;
using TMDbLib.Objects.TvShows;

View File

@ -1,4 +1,4 @@
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TMDbLib.Objects.Companies;
using TMDbLib.Objects.Search;

View File

@ -17,7 +17,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
</ProjectReference>
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using Autofac;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers;
using Kyoo.TheMovieDb.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.TheMovieDb
{
@ -25,55 +22,15 @@ namespace Kyoo.TheMovieDb
public string Description => "A metadata provider for TheMovieDB.";
/// <inheritdoc />
public ICollection<Type> Provides => new []
public Dictionary<string, Type> Configuration => new()
{
typeof(IMetadataProvider)
{ TheMovieDbOptions.Path, typeof(TheMovieDbOptions) }
};
/// <inheritdoc />
public ICollection<ConditionalProvide> ConditionalProvides => ArraySegment<ConditionalProvide>.Empty;
/// <inheritdoc />
public ICollection<Type> Requires => ArraySegment<Type>.Empty;
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
/// Create a new tmdb module instance and use the given configuration.
/// </summary>
/// <param name="configuration">The configuration to use</param>
public PluginTmdb(IConfiguration configuration)
{
_configuration = configuration;
}
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
{
builder.RegisterProvider<TheMovieDbProvider>();
}
/// <inheritdoc />
public void Configure(IServiceCollection services, ICollection<Type> availableTypes)
{
services.Configure<TheMovieDbOptions>(_configuration.GetSection(TheMovieDbOptions.Path));
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
{
ConfigurationManager.AddTyped<TheMovieDbOptions>(TheMovieDbOptions.Path);
}
}
}

View File

@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.TheMovieDb.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

View File

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Kyoo.Models;
using Kyoo.Abstractions.Models;
using TvDbSharper.Dto;
namespace Kyoo.TheTvdb

View File

@ -16,7 +16,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
</ProjectReference>
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using Autofac;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Abstractions;
using Kyoo.Abstractions.Controllers;
using Kyoo.TheTvdb.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TvDbSharper;
namespace Kyoo.TheTvdb
@ -24,58 +21,18 @@ namespace Kyoo.TheTvdb
/// <inheritdoc />
public string Description => "A metadata provider for The TVDB.";
/// <inheritdoc />
public ICollection<Type> Provides => new []
public Dictionary<string, Type> Configuration => new()
{
typeof(IMetadataProvider)
{ TvdbOption.Path, typeof(TvdbOption) }
};
/// <inheritdoc />
public ICollection<ConditionalProvide> ConditionalProvides => ArraySegment<ConditionalProvide>.Empty;
/// <inheritdoc />
public ICollection<Type> Requires => ArraySegment<Type>.Empty;
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
/// Create a new tvdb module instance and use the given configuration.
/// </summary>
/// <param name="configuration">The configuration to use</param>
public PluginTvdb(IConfiguration configuration)
{
_configuration = configuration;
}
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
{
builder.RegisterType<TvDbClient>().As<ITvDbClient>();
builder.RegisterProvider<ProviderTvdb>();
}
/// <inheritdoc />
public void Configure(IServiceCollection services, ICollection<Type> availableTypes)
{
services.Configure<TvdbOption>(_configuration.GetSection(TvdbOption.Path));
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
{
ConfigurationManager.AddTyped<TvdbOption>(TvdbOption.Path);
}
}
}

View File

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Controllers;
using Kyoo.Models;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.TheTvdb.Models;
using Microsoft.Extensions.Options;
using TvDbSharper;

View File

@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules/**</DefaultItemExcludes>
<SpaRoot>Front/</SpaRoot>
<!-- Set this to true if you enable server-side prerendering -->
<BuildServerSideRenderer>false</BuildServerSideRenderer>
<Company>SDG</Company>
<Authors>Zoe Roux</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.8" />
<ProjectReference Include="../Kyoo.Abstractions/Kyoo.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<Content Remove="$(SpaRoot)**" />
<Compile Remove="$(SpaRoot)**" />
<Content Include="$(SpaRoot)static/**" Visible="false">
<Link>wwwroot/%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="$(SpaRoot)dist/**; $(SpaRoot)dist-server/**" />
<Content Include="$(SpaRoot)dist/**; $(SpaRoot)dist-server/**" Visible="false" Condition="'$(Configuration)' != 'Debug'">
<Link>wwwroot/%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build"
Condition="'$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') And '$(SkipWebApp)' != 'true'">
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" BeforeTargets="Build" Condition="'$(SkipWebApp)' != 'true' And '$(Configuration)' != 'Debug'">
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition="'$(BuildServerSideRenderer)' == 'true'" />
</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="mklink /D '$(OutDir)$(ProjectName)' '$(ProjectDir)'" Condition="$(OS) != 'Unix'" />
</Target>
</Project>

110
Kyoo.WebApp/WebAppModule.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.IO;
using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Kyoo.WebApp
{
/// <summary>
/// A module to enable the web-app (the front-end of kyoo).
/// </summary>
public class WebAppModule : IPlugin
{
/// <inheritdoc />
public string Slug => "webapp";
/// <inheritdoc />
public string Name => "WebApp";
/// <inheritdoc />
public string Description => "A module to enable the web-app (the front-end of kyoo).";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <inheritdoc />
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>
/// Create a new <see cref="WebAppModule"/>.
/// </summary>
/// <param name="logger">A logger only used to inform the user if the webapp could not be enabled.</param>
public WebAppModule(ILogger<WebAppModule> logger)
{
_logger = logger;
}
/// <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 />
public void Configure(IServiceCollection services)
{
services.AddSpaStaticFiles(x =>
{
x.RootPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
});
}
/// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps => new IStartupAction[]
{
SA.New<IApplicationBuilder, IWebHostEnvironment>((app, env) =>
{
if (!env.IsDevelopment())
app.UseSpaStaticFiles();
}, SA.StaticFiles),
SA.New<IApplicationBuilder, IContentTypeProvider>((app, contentTypeProvider) =>
{
app.UseStaticFiles(new StaticFileOptions
{
ContentTypeProvider = contentTypeProvider,
FileProvider = new PhysicalFileProvider(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"))
});
}, SA.StaticFiles),
SA.New<IApplicationBuilder>(app =>
{
app.Use((ctx, next) =>
{
ctx.Response.Headers.Remove("X-Powered-By");
ctx.Response.Headers.Remove("Server");
ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen");
ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self' blob:; script-src 'self' blob: 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src 'self'");
ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
ctx.Response.Headers.Add("Referrer-Policy", "no-referrer");
ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null");
ctx.Response.Headers.Add("X-Content-Type-Options", "nosniff");
return next();
});
}, SA.Endpoint - 499),
SA.New<IApplicationBuilder, IWebHostEnvironment>((app, env) =>
{
app.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp", "Front");
if (env.IsDevelopment())
spa.UseAngularCliServer("start");
});
}, SA.Endpoint - 500)
};
}
}

View File

@ -1,9 +1,9 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kyoo", "Kyoo\Kyoo.csproj", "{0F8275B6-C7DD-42DF-A168-755C81B1C329}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Common", "Kyoo.Common\Kyoo.Common.csproj", "{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Abstractions", "Kyoo.Abstractions\Kyoo.Abstractions.csproj", "{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.CommonAPI", "Kyoo.CommonAPI\Kyoo.CommonAPI.csproj", "{6F91B645-F785-46BB-9C4F-1EFC83E489B6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Database", "Kyoo.Database\Kyoo.Database.csproj", "{6F91B645-F785-46BB-9C4F-1EFC83E489B6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Postgresql", "Kyoo.Postgresql\Kyoo.Postgresql.csproj", "{3213C96D-0BF3-460B-A8B5-B9977229408A}"
EndProject
@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.TheMovieDb", "Kyoo.The
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Tests", "tests\Kyoo.Tests\Kyoo.Tests.csproj", "{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.WebApp", "Kyoo.WebApp\Kyoo.WebApp.csproj", "{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -59,5 +61,9 @@ Global
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.Build.0 = Release|Any CPU
{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -5,9 +5,10 @@ using System.Dynamic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Api;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
@ -50,6 +51,21 @@ namespace Kyoo.Controllers
ConfigurationReference config = ConfigurationReference.CreateUntyped(path);
_references.Add(config.Path, config.Type);
}
/// <inheritdoc />
public void Register(string path, Type type)
{
if (type == null)
{
ConfigurationReference config = ConfigurationReference.CreateUntyped(path);
_references.Add(config.Path, config.Type);
}
else
{
foreach (ConfigurationReference confRef in ConfigurationReference.CreateReference(path, type))
_references.Add(confRef.Path, confRef.Type);
}
}
/// <summary>
/// Get the type of the resource at the given path

Some files were not shown because too many files have changed in this diff Show More