diff --git a/Kyoo.Common/Controllers/IPlugin.cs b/Kyoo.Common/Controllers/IPlugin.cs
new file mode 100644
index 00000000..a6949083
--- /dev/null
+++ b/Kyoo.Common/Controllers/IPlugin.cs
@@ -0,0 +1,39 @@
+namespace Kyoo.Controllers
+{
+ ///
+ /// A common interface used to discord plugins
+ ///
+ public interface IPlugin
+ {
+ ///
+ /// A slug to identify this plugin in queries.
+ ///
+ string Slug { get; }
+
+ ///
+ /// The name of the plugin
+ ///
+ string Name { get; }
+
+ ///
+ /// The description of this plugin. This will be displayed on the "installed plugins" page.
+ ///
+ string Description { get; }
+
+
+ ///
+ /// A configure method that will be runned on plugin's startup.
+ ///
+ ///
+ /// You can have use any services as parameter, they will be injected from the service provider
+ /// You can add managed types or any type you like using the IUnityContainer like so:
+ ///
+ /// public static void Configure(IUnityContainer services)
+ /// {
+ /// services.AddTask<MyTask>()
+ /// }
+ ///
+ ///
+ static void Configure() { }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo.Common/Controllers/ITask.cs b/Kyoo.Common/Controllers/ITask.cs
new file mode 100644
index 00000000..6ac064e9
--- /dev/null
+++ b/Kyoo.Common/Controllers/ITask.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Kyoo.Models.Attributes;
+
+namespace Kyoo.Controllers
+{
+ ///
+ /// A single task parameter. This struct contains metadata to display and utility functions to get them in the taks.
+ ///
+ /// This struct will be used to generate the swagger documentation of the task.
+ public record TaskParameter
+ {
+ ///
+ /// The name of this parameter.
+ ///
+ public string Name { get; init; }
+
+ ///
+ /// The description of this parameter.
+ ///
+ public string Description { get; init; }
+
+ ///
+ /// The type of this parameter.
+ ///
+ public Type Type { get; init; }
+
+ ///
+ /// Is this parameter required or can it be ignored?
+ ///
+ public bool IsRequired { get; init; }
+
+ ///
+ /// The default value of this object.
+ ///
+ public object DefaultValue { get; init; }
+
+ ///
+ /// The value of the parameter.
+ ///
+ private object Value { get; init; }
+
+ ///
+ /// Create a new task parameter.
+ ///
+ /// The name of the parameter
+ /// The description of the parameter
+ /// The type of the parameter.
+ /// A new task parameter.
+ public static TaskParameter Create(string name, string description)
+ {
+ return new()
+ {
+ Name = name,
+ Description = description,
+ Type = typeof(T)
+ };
+ }
+
+ ///
+ /// Create a parameter's value to give to a task.
+ ///
+ /// The name of the parameter
+ /// The value of the parameter. It's type will be used as parameter's type.
+ /// The type of the parameter
+ /// A TaskParameter that can be used as value.
+ public static TaskParameter CreateValue(string name, T value)
+ {
+ return new()
+ {
+ Name = name,
+ Type = typeof(T),
+ Value = value
+ };
+ }
+
+ ///
+ /// Create a parameter's value for the current parameter.
+ ///
+ /// The value to use
+ /// A new parameter's value for this current parameter
+ public TaskParameter CreateValue(object value)
+ {
+ return this with {Value = value};
+ }
+
+ ///
+ /// Get the value of this parameter. If the value is of the wrong type, it will be converted.
+ ///
+ /// The type of this parameter
+ /// The value of this parameter.
+ public T As()
+ {
+ return (T)Convert.ChangeType(Value, typeof(T));
+ }
+ }
+
+ ///
+ /// A parameters container implementing an indexer to allow simple usage of parameters.
+ ///
+ public class TaskParameters : List
+ {
+ ///
+ /// An indexer that return the parameter with the specified name.
+ ///
+ /// The name of the task (case sensitive)
+ public TaskParameter this[string name] => this.FirstOrDefault(x => x.Name == name);
+
+
+ ///
+ /// Create a new, empty,
+ ///
+ public TaskParameters() {}
+
+ ///
+ /// Create a with an initial parameters content
+ ///
+ /// The list of parameters
+ public TaskParameters(IEnumerable parameters)
+ {
+ AddRange(parameters);
+ }
+ }
+
+ ///
+ /// A common interface that tasks should implement.
+ ///
+ public interface ITask
+ {
+ ///
+ /// The slug of the task, used to start it.
+ ///
+ public string Slug { get; }
+
+ ///
+ /// The name of the task that will be displayed to the user.
+ ///
+ public string Name { get; }
+
+ ///
+ /// A quick description of what this task will do.
+ ///
+ public string Description { get; }
+
+ ///
+ /// An optional message to display to help the user.
+ ///
+ public string HelpMessage { get; }
+
+ ///
+ /// Should this task be automatically runned at app startup?
+ ///
+ public bool RunOnStartup { get; }
+
+ ///
+ /// The priority of this task. Only used if is true.
+ /// It allow one to specify witch task will be started first as tasked are run on a Priority's descending order.
+ ///
+ public int Priority { get; }
+
+ ///
+ /// Start this task.
+ ///
+ /// The list of parameters.
+ /// A token to request the task's cancelation.
+ /// If this task is not cancelled quickly, it might be killed by the runner.
+ ///
+ /// Your task can have any service as a public field and use the ,
+ /// they will be set to an available service from the service container before calling this method.
+ ///
+ public Task Run(TaskParameters arguments, CancellationToken cancellationToken);
+
+ ///
+ /// The list of parameters
+ ///
+ /// All parameters that this task as. Every one of them will be given to the run function with a value.
+ public TaskParameters GetParameters();
+
+ ///
+ /// If this task is running, return the percentage of completion of this task or null if no percentage can be given.
+ ///
+ /// The percentage of completion of the task.
+ public int? Progress();
+ }
+}
\ No newline at end of file
diff --git a/Kyoo.Common/Controllers/ITaskManager.cs b/Kyoo.Common/Controllers/ITaskManager.cs
index 5c926eeb..7f3b6013 100644
--- a/Kyoo.Common/Controllers/ITaskManager.cs
+++ b/Kyoo.Common/Controllers/ITaskManager.cs
@@ -1,13 +1,40 @@
+using System;
using System.Collections.Generic;
using Kyoo.Models;
+using Kyoo.Models.Exceptions;
namespace Kyoo.Controllers
{
+ ///
+ /// A service to handle long running tasks.
+ ///
+ /// The concurrent number of running tasks is implementation dependent.
public interface ITaskManager
{
- bool StartTask(string taskSlug, string arguments = null);
- ITask GetRunningTask();
- void ReloadTask();
- IEnumerable GetAllTasks();
+ ///
+ /// Start a new task (or queue it).
+ ///
+ /// The slug of the task to run
+ /// A list of arguments to pass to the task. An automatic conversion will be made if arguments to not fit.
+ /// If the number of arguments is invalid or if an argument can't be converted.
+ /// The task could not be found.
+ void StartTask(string taskSlug, Dictionary arguments);
+
+ ///
+ /// Get all currently running tasks
+ ///
+ /// A list of currently running tasks.
+ ICollection GetRunningTasks();
+
+ ///
+ /// Get all availables tasks
+ ///
+ /// A list of every tasks that this instance know.
+ ICollection GetAllTasks();
+
+ ///
+ /// Reload tasks and run startup tasks.
+ ///
+ void ReloadTasks();
}
}
\ No newline at end of file
diff --git a/Kyoo.Common/Kyoo.Common.csproj b/Kyoo.Common/Kyoo.Common.csproj
index 21d71761..3f4fdbba 100644
--- a/Kyoo.Common/Kyoo.Common.csproj
+++ b/Kyoo.Common/Kyoo.Common.csproj
@@ -24,6 +24,13 @@
+
+
+
+
+
+ ..\..\..\..\..\..\usr\share\dotnet\shared\Microsoft.AspNetCore.App\5.0.5\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
diff --git a/Kyoo.Common/Models/Attributes/InjectedAttribute.cs b/Kyoo.Common/Models/Attributes/InjectedAttribute.cs
new file mode 100644
index 00000000..af76eb88
--- /dev/null
+++ b/Kyoo.Common/Models/Attributes/InjectedAttribute.cs
@@ -0,0 +1,14 @@
+using System;
+using Kyoo.Controllers;
+
+namespace Kyoo.Models.Attributes
+{
+ ///
+ /// An attribute to inform that the service will be injected automatically by a service provider.
+ ///
+ ///
+ /// It should only be used on and will be injected before calling
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class InjectedAttribute : Attribute { }
+}
\ No newline at end of file
diff --git a/Kyoo.Common/Models/Plugin.cs b/Kyoo.Common/Models/Plugin.cs
deleted file mode 100644
index 7947d542..00000000
--- a/Kyoo.Common/Models/Plugin.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-
-namespace Kyoo.Models
-{
- public interface IPlugin
- {
- public string Name { get; }
- public ICollection Tasks { get; }
- }
-}
\ No newline at end of file
diff --git a/Kyoo.Common/Models/Task.cs b/Kyoo.Common/Models/Task.cs
deleted file mode 100644
index 76cfbc52..00000000
--- a/Kyoo.Common/Models/Task.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Kyoo.Models
-{
- public interface ITask
- {
- public string Slug { get; }
- public string Name { get; }
- public string Description { get; }
- public string HelpMessage { get; }
- public bool RunOnStartup { get; }
- public int Priority { get; }
- public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null);
- public Task> GetPossibleParameters();
- public int? Progress();
- }
-}
\ No newline at end of file
diff --git a/Kyoo.Common/Module.cs b/Kyoo.Common/Module.cs
new file mode 100644
index 00000000..41b5859c
--- /dev/null
+++ b/Kyoo.Common/Module.cs
@@ -0,0 +1,24 @@
+using Kyoo.Controllers;
+using Unity;
+
+namespace Kyoo
+{
+ ///
+ /// A static class with helper functions to setup external modules
+ ///
+ public static class Module
+ {
+ ///
+ /// Register a new task to the container.
+ ///
+ /// The container
+ /// The type of the task
+ /// The initial container.
+ public static IUnityContainer AddTask(this IUnityContainer services)
+ where T : class, ITask
+ {
+ services.RegisterSingleton();
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo.Common/Utility.cs b/Kyoo.Common/Utility.cs
index 220b2003..3e4bba15 100644
--- a/Kyoo.Common/Utility.cs
+++ b/Kyoo.Common/Utility.cs
@@ -529,9 +529,9 @@ namespace Kyoo
await action(i);
}
- private static MethodInfo GetMethod(Type type, BindingFlags flag, string name, Type[] generics, object[] args)
+ public static MethodInfo GetMethod(Type type, BindingFlags flag, string name, Type[] generics, object[] args)
{
- MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public | BindingFlags.NonPublic)
+ MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public)
.Where(x => x.Name == name)
.Where(x => x.GetGenericArguments().Length == generics.Length)
.Where(x => x.GetParameters().Length == args.Length)
diff --git a/Kyoo/Controllers/TaskManager.cs b/Kyoo/Controllers/TaskManager.cs
index 519fb43f..e93c050f 100644
--- a/Kyoo/Controllers/TaskManager.cs
+++ b/Kyoo/Controllers/TaskManager.cs
@@ -1,56 +1,129 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-using Kyoo.Models;
-using Kyoo.Tasks;
+using Kyoo.Models.Attributes;
+using Kyoo.Models.Exceptions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Unity;
namespace Kyoo.Controllers
{
+ ///
+ /// A service to handle long running tasks and a background runner.
+ ///
+ /// Task will be queued, only one can run simultaneously.
public class TaskManager : BackgroundService, ITaskManager
{
- private readonly IServiceProvider _serviceProvider;
- private readonly IPluginManager _pluginManager;
+ ///
+ /// The service provider used to activate
+ ///
+ private readonly IUnityContainer _container;
+ ///
+ /// The configuration instance used to get schedule informations
+ ///
private readonly IConfiguration _configuration;
-
- private List<(ITask task, DateTime scheduledDate)> _tasks = new List<(ITask, DateTime)>();
- private CancellationTokenSource _taskToken = new CancellationTokenSource();
+ ///
+ /// The logger instance.
+ ///
+ private readonly ILogger _logger;
+
+ ///
+ /// The list of tasks and their next scheduled run.
+ ///
+ private List<(ITask task, DateTime scheduledDate)> _tasks;
+ ///
+ /// The queue of tasks that should be runned as soon as possible.
+ ///
+ private readonly Queue<(ITask, Dictionary)> _queuedTasks = new();
+ ///
+ /// The currently running task.
+ ///
private ITask _runningTask;
- private Queue<(ITask, string)> _queuedTasks = new Queue<(ITask, string)>();
+ ///
+ /// The cancellation token used to cancel the running task when the runner should shutdown.
+ ///
+ private readonly CancellationTokenSource _taskToken = new();
- public TaskManager(IServiceProvider serviceProvider, IPluginManager pluginManager, IConfiguration configuration)
+
+ ///
+ /// Create a new .
+ ///
+ /// The list of tasks to manage
+ /// The service provider to request services for tasks
+ /// The configuration to load schedule information.
+ /// The logger.
+ public TaskManager(IEnumerable tasks,
+ IUnityContainer container,
+ IConfiguration configuration,
+ ILogger logger)
{
- _serviceProvider = serviceProvider;
- _pluginManager = pluginManager;
- _configuration = configuration;
+ _tasks = tasks.Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))).ToList();
+ _container = container;
+ _configuration = configuration.GetSection("scheduledTasks");
+ _logger = logger;
}
+
+ ///
+ /// Triggered when the application host is ready to start the service.
+ ///
+ /// Start the runner in another thread.
+ /// Indicates that the start process has been aborted.
+ public override Task StartAsync(CancellationToken cancellationToken)
+ {
+ Task.Run(() => base.StartAsync(cancellationToken), CancellationToken.None);
+ return Task.CompletedTask;
+ }
+
+ ///
+ public override Task StopAsync(CancellationToken cancellationToken)
+ {
+ _taskToken.Cancel();
+ return base.StopAsync(cancellationToken);
+ }
+
+ ///
+ /// The runner that will host tasks and run queued tasks.
+ ///
+ /// A token to stop the runner
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
- ReloadTask();
-
- IEnumerable startupTasks = _tasks.Select(x => x.task)
- .Where(x => x.RunOnStartup && x.Priority != Int32.MaxValue)
- .OrderByDescending(x => x.Priority);
- foreach (ITask task in startupTasks)
- _queuedTasks.Enqueue((task, null));
+ EnqueueStartupTasks();
while (!cancellationToken.IsCancellationRequested)
{
if (_queuedTasks.Any())
{
- (ITask task, string arguments) = _queuedTasks.Dequeue();
+ (ITask task, Dictionary arguments) = _queuedTasks.Dequeue();
_runningTask = task;
try
{
- await task.Run(_serviceProvider, _taskToken.Token, arguments);
+ ICollection all = task.GetParameters();
+ TaskParameters args = new(arguments
+ .Select(x => (value: x, arg: all
+ .FirstOrDefault(y => string.Equals(y.Name, x.Key, StringComparison.OrdinalIgnoreCase))))
+ .Select(x =>
+ {
+ if (x.arg == null)
+ throw new ArgumentException($"Invalid argument name: {x.value.Key}");
+ return x.arg.CreateValue(x.value.Value);
+ }));
+
+
+ _logger.LogInformation("Task starting: {Task}", task.Name);
+ InjectServices(task);
+ await task.Run(args, _taskToken.Token);
+ _logger.LogInformation("Task finished: {Task}", task.Name);
}
catch (Exception e)
{
- Console.Error.WriteLine($"An unhandled exception occured while running the task {task.Name}.\nInner exception: {e.Message}\n\n");
+ _logger.LogError("An unhandled exception occured while running the task {Task}.\n" +
+ "Inner exception: {Exception}\n\n", task.Name, e.Message);
}
}
else
@@ -61,67 +134,90 @@ namespace Kyoo.Controllers
}
}
+ ///
+ /// Inject services into the marked properties of the given object.
+ ///
+ /// The object to inject
+ /// The type of the object.
+ private void InjectServices(T obj)
+ {
+ IEnumerable properties = typeof(T).GetProperties()
+ .Where(x => x.GetCustomAttribute() != null)
+ .Where(x => x.CanWrite);
+
+ foreach (PropertyInfo property in properties)
+ {
+ object value = _container.Resolve(property.PropertyType);
+ property.SetValue(obj, value);
+ }
+ }
+
+ ///
+ /// Start tasks that are scheduled for start.
+ ///
private void QueueScheduledTasks()
{
IEnumerable tasksToQueue = _tasks.Where(x => x.scheduledDate <= DateTime.Now)
.Select(x => x.task.Slug);
foreach (string task in tasksToQueue)
- StartTask(task);
+ {
+ _logger.LogDebug("Queuing task scheduled for running: {Task}", task);
+ StartTask(task, new Dictionary());
+ }
}
- public override Task StartAsync(CancellationToken cancellationToken)
+ ///
+ /// Queue startup tasks with respect to the priority rules.
+ ///
+ private void EnqueueStartupTasks()
{
- Task.Run(() => base.StartAsync(cancellationToken));
- return Task.CompletedTask;
+ IEnumerable startupTasks = _tasks.Select(x => x.task)
+ .Where(x => x.RunOnStartup && x.Priority != int.MaxValue)
+ .OrderByDescending(x => x.Priority);
+ foreach (ITask task in startupTasks)
+ _queuedTasks.Enqueue((task, null));
}
- public override Task StopAsync(CancellationToken cancellationToken)
- {
- _taskToken.Cancel();
- return base.StopAsync(cancellationToken);
- }
-
- public bool StartTask(string taskSlug, string arguments = null)
+ ///
+ public void StartTask(string taskSlug, Dictionary arguments)
{
int index = _tasks.FindIndex(x => x.task.Slug == taskSlug);
if (index == -1)
- return false;
+ throw new ItemNotFound($"No task found with the slug {taskSlug}");
_queuedTasks.Enqueue((_tasks[index].task, arguments));
_tasks[index] = (_tasks[index].task, DateTime.Now + GetTaskDelay(taskSlug));
- return true;
}
- public TimeSpan GetTaskDelay(string taskSlug)
+ ///
+ /// Get the delay of a task
+ ///
+ /// The slug of the task
+ /// The delay of the task.
+ private TimeSpan GetTaskDelay(string taskSlug)
{
- TimeSpan delay = _configuration.GetSection("scheduledTasks").GetValue(taskSlug);
+ TimeSpan delay = _configuration.GetValue(taskSlug);
if (delay == default)
- delay = TimeSpan.FromDays(365);
+ delay = TimeSpan.MaxValue;
return delay;
}
- public ITask GetRunningTask()
+ ///
+ public ICollection GetRunningTasks()
{
- return _runningTask;
+ return new[] {_runningTask};
}
- public void ReloadTask()
+ ///
+ public ICollection GetAllTasks()
{
- _tasks.Clear();
- _tasks.AddRange(CoreTaskHolder.Tasks.Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))));
-
- IEnumerable prerunTasks = _tasks.Select(x => x.task)
- .Where(x => x.RunOnStartup && x.Priority == int.MaxValue);
-
- foreach (ITask task in prerunTasks)
- task.Run(_serviceProvider, _taskToken.Token);
- foreach (IPlugin plugin in _pluginManager.GetAllPlugins())
- if (plugin.Tasks != null)
- _tasks.AddRange(plugin.Tasks.Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))));
+ return _tasks.Select(x => x.task).ToArray();
}
- public IEnumerable GetAllTasks()
+ ///
+ public void ReloadTasks()
{
- return _tasks.Select(x => x.task);
+ _tasks = _container.ResolveAll().Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))).ToList();
+ EnqueueStartupTasks();
}
}
}
\ No newline at end of file
diff --git a/Kyoo/CoreModule.cs b/Kyoo/CoreModule.cs
new file mode 100644
index 00000000..d194a267
--- /dev/null
+++ b/Kyoo/CoreModule.cs
@@ -0,0 +1,26 @@
+using Kyoo.Controllers;
+using Unity;
+
+namespace Kyoo
+{
+ ///
+ /// The core module ccontaining default implementations
+ ///
+ public class CoreModule : IPlugin
+ {
+ ///
+ public string Slug => "core";
+
+ ///
+ public string Name => "Core";
+
+ ///
+ public string Description => "The core module containing default implementations.";
+
+ ///
+ public static void Configure(IUnityContainer container)
+ {
+ container.AddTask();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo/Kyoo.csproj b/Kyoo/Kyoo.csproj
index 2d624b9c..92c27dba 100644
--- a/Kyoo/Kyoo.csproj
+++ b/Kyoo/Kyoo.csproj
@@ -33,6 +33,8 @@
+
+
@@ -56,7 +58,6 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
diff --git a/Kyoo/Program.cs b/Kyoo/Program.cs
index 1fa2b7c3..ee13bcdb 100644
--- a/Kyoo/Program.cs
+++ b/Kyoo/Program.cs
@@ -1,13 +1,14 @@
using System;
using System.IO;
using System.Threading.Tasks;
-using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.StaticWebAssets;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Unity;
+using Unity.Microsoft.DependencyInjection;
namespace Kyoo
{
@@ -39,6 +40,7 @@ namespace Kyoo
if (debug == null && Environment.GetEnvironmentVariable("ENVIRONMENT") != null)
Console.WriteLine($"Invalid ENVIRONMENT variable. Supported values are \"debug\" and \"prod\". Ignoring...");
+
#if DEBUG
debug ??= true;
#endif
@@ -70,7 +72,8 @@ namespace Kyoo
/// A new web host instance
private static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
- WebHost.CreateDefaultBuilder(args);
+ UnityContainer container = new();
+ container.EnableDebugDiagnostic();
return new WebHostBuilder()
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
@@ -89,6 +92,7 @@ namespace Kyoo
if (context.HostingEnvironment.IsDevelopment())
StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration);
})
+ .UseUnityServiceProvider(container)
.ConfigureServices(x => x.AddRouting())
.UseKestrel(options => { options.AddServerHeader = false; })
.UseIIS()
diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs
index 8e1485e3..5f259d24 100644
--- a/Kyoo/Startup.cs
+++ b/Kyoo/Startup.cs
@@ -20,6 +20,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Unity;
namespace Kyoo
{
@@ -185,7 +186,7 @@ namespace Kyoo
services.AddHostedService(provider => (TaskManager)provider.GetService());
}
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IUnityContainer container)
{
if (env.IsDevelopment())
{
@@ -247,6 +248,9 @@ namespace Kyoo
if (env.IsDevelopment())
spa.UseAngularCliServer("start");
});
+
+ CoreModule.Configure(container);
+ container.Resolve().ReloadTasks();
}
}
}
diff --git a/Kyoo/Tasks/CoreTaskHolder.cs b/Kyoo/Tasks/CoreTaskHolder.cs
deleted file mode 100644
index 867c83f4..00000000
--- a/Kyoo/Tasks/CoreTaskHolder.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Kyoo.Controllers;
-using Kyoo.Models;
-
-namespace Kyoo.Tasks
-{
- public static class CoreTaskHolder
- {
- public static readonly ITask[] Tasks =
- {
- new CreateDatabase(),
- new PluginLoader(),
- new Crawler(),
- new MetadataProviderLoader(),
- // new ReScan(),
- new ExtractMetadata()
- };
- }
-}
\ No newline at end of file
diff --git a/Kyoo/Tasks/Crawler.cs b/Kyoo/Tasks/Crawler.cs
index 47c931c9..d86f54d9 100644
--- a/Kyoo/Tasks/Crawler.cs
+++ b/Kyoo/Tasks/Crawler.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
+using Kyoo.Models.Attributes;
using Kyoo.Models.Exceptions;
using Microsoft.Extensions.DependencyInjection;
@@ -21,19 +22,20 @@ namespace Kyoo.Controllers
public bool RunOnStartup => true;
public int Priority => 0;
- private IServiceProvider _serviceProvider;
- private IThumbnailsManager _thumbnailsManager;
- private IProviderManager _metadataProvider;
- private ITranscoder _transcoder;
- private IConfiguration _config;
+ [Injected] public IServiceProvider ServiceProvider { private get; set; }
+ [Injected] public IThumbnailsManager ThumbnailsManager { private get; set; }
+ [Injected] public IProviderManager MetadataProvider { private get; set; }
+ [Injected] public ITranscoder Transcoder { private get; set; }
+ [Injected] public IConfiguration Config { private get; set; }
private int _parallelTasks;
- public async Task> GetPossibleParameters()
+ public TaskParameters GetParameters()
{
- using IServiceScope serviceScope = _serviceProvider.CreateScope();
- ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService();
- return (await libraryManager!.GetAll()).Select(x => x.Slug);
+ return new()
+ {
+ TaskParameter.Create("slug", "A library slug to restrict the scan to this library.")
+ };
}
public int? Progress()
@@ -42,20 +44,16 @@ namespace Kyoo.Controllers
return null;
}
- public async Task Run(IServiceProvider serviceProvider,
- CancellationToken cancellationToken,
- string argument = null)
+ public async Task Run(TaskParameters parameters,
+ CancellationToken cancellationToken)
{
- _serviceProvider = serviceProvider;
- _thumbnailsManager = serviceProvider.GetService();
- _metadataProvider = serviceProvider.GetService();
- _transcoder = serviceProvider.GetService();
- _config = serviceProvider.GetService();
- _parallelTasks = _config.GetValue("parallelTasks");
+ string argument = parameters["slug"].As();
+
+ _parallelTasks = Config.GetValue("parallelTasks");
if (_parallelTasks <= 0)
_parallelTasks = 30;
- using IServiceScope serviceScope = _serviceProvider.CreateScope();
+ using IServiceScope serviceScope = ServiceProvider.CreateScope();
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService();
foreach (Show show in await libraryManager!.GetAll())
@@ -148,10 +146,10 @@ namespace Kyoo.Controllers
{
if (token.IsCancellationRequested || path.Split(Path.DirectorySeparatorChar).Contains("Subtitles"))
return;
- using IServiceScope serviceScope = _serviceProvider.CreateScope();
+ using IServiceScope serviceScope = ServiceProvider.CreateScope();
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService();
- string patern = _config.GetValue("subtitleRegex");
+ string patern = Config.GetValue("subtitleRegex");
Regex regex = new(patern, RegexOptions.IgnoreCase);
Match match = regex.Match(path);
@@ -195,10 +193,10 @@ namespace Kyoo.Controllers
try
{
- using IServiceScope serviceScope = _serviceProvider.CreateScope();
+ using IServiceScope serviceScope = ServiceProvider.CreateScope();
ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService();
- string patern = _config.GetValue("regex");
+ string patern = Config.GetValue("regex");
Regex regex = new(patern, RegexOptions.IgnoreCase);
Match match = regex.Match(relativePath);
@@ -257,7 +255,7 @@ namespace Kyoo.Controllers
Collection collection = await libraryManager.Get(Utility.ToSlug(collectionName));
if (collection != null)
return collection;
- collection = await _metadataProvider.GetCollectionFromName(collectionName, library);
+ collection = await MetadataProvider.GetCollectionFromName(collectionName, library);
try
{
@@ -282,9 +280,9 @@ namespace Kyoo.Controllers
await libraryManager.Load(old, x => x.ExternalIDs);
return old;
}
- Show show = await _metadataProvider.SearchShow(showTitle, isMovie, library);
+ Show show = await MetadataProvider.SearchShow(showTitle, isMovie, library);
show.Path = showPath;
- show.People = await _metadataProvider.GetPeople(show, library);
+ show.People = await MetadataProvider.GetPeople(show, library);
try
{
@@ -301,7 +299,7 @@ namespace Kyoo.Controllers
show.Slug += $"-{show.StartYear}";
await libraryManager.Create(show);
}
- await _thumbnailsManager.Validate(show);
+ await ThumbnailsManager.Validate(show);
return show;
}
@@ -320,9 +318,9 @@ namespace Kyoo.Controllers
}
catch (ItemNotFound)
{
- Season season = await _metadataProvider.GetSeason(show, seasonNumber, library);
+ Season season = await MetadataProvider.GetSeason(show, seasonNumber, library);
await libraryManager.CreateIfNotExists(season);
- await _thumbnailsManager.Validate(season);
+ await ThumbnailsManager.Validate(season);
season.Show = show;
return season;
}
@@ -336,7 +334,7 @@ namespace Kyoo.Controllers
string episodePath,
Library library)
{
- Episode episode = await _metadataProvider.GetEpisode(show,
+ Episode episode = await MetadataProvider.GetEpisode(show,
episodePath,
season?.SeasonNumber ?? -1,
episodeNumber,
@@ -346,7 +344,7 @@ namespace Kyoo.Controllers
season ??= await GetSeason(libraryManager, show, episode.SeasonNumber, library);
episode.Season = season;
episode.SeasonID = season?.ID;
- await _thumbnailsManager.Validate(episode);
+ await ThumbnailsManager.Validate(episode);
await GetTracks(episode);
return episode;
}
@@ -367,7 +365,7 @@ namespace Kyoo.Controllers
private async Task> GetTracks(Episode episode)
{
- episode.Tracks = (await _transcoder.ExtractInfos(episode, false))
+ episode.Tracks = (await Transcoder.ExtractInfos(episode, false))
.Where(x => x.Type != StreamType.Attachment)
.ToArray();
return episode.Tracks;
diff --git a/Kyoo/Tasks/CreateDatabase.cs b/Kyoo/Tasks/CreateDatabase.cs
index 2264b116..8d54cd87 100644
--- a/Kyoo/Tasks/CreateDatabase.cs
+++ b/Kyoo/Tasks/CreateDatabase.cs
@@ -1,66 +1,66 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using IdentityServer4.EntityFramework.DbContexts;
-using IdentityServer4.EntityFramework.Mappers;
-using IdentityServer4.Models;
-using Kyoo.Models;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Kyoo.Tasks
-{
- public class CreateDatabase : ITask
- {
- public string Slug => "create-database";
- public string Name => "Create the database";
- public string Description => "Create the database if it does not exit and initialize it with defaults value.";
- public string HelpMessage => null;
- public bool RunOnStartup => true;
- public int Priority => int.MaxValue;
-
- public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
- {
- using IServiceScope serviceScope = serviceProvider.CreateScope();
- DatabaseContext databaseContext = serviceScope.ServiceProvider.GetService();
- IdentityDatabase identityDatabase = serviceScope.ServiceProvider.GetService();
- ConfigurationDbContext identityContext = serviceScope.ServiceProvider.GetService();
-
- databaseContext!.Database.Migrate();
- identityDatabase!.Database.Migrate();
- identityContext!.Database.Migrate();
-
- if (!identityContext.Clients.Any())
- {
- foreach (Client client in IdentityContext.GetClients())
- identityContext.Clients.Add(client.ToEntity());
- identityContext.SaveChanges();
- }
- if (!identityContext.IdentityResources.Any())
- {
- foreach (IdentityResource resource in IdentityContext.GetIdentityResources())
- identityContext.IdentityResources.Add(resource.ToEntity());
- identityContext.SaveChanges();
- }
- if (!identityContext.ApiResources.Any())
- {
- foreach (ApiResource resource in IdentityContext.GetApis())
- identityContext.ApiResources.Add(resource.ToEntity());
- identityContext.SaveChanges();
- }
- return Task.CompletedTask;
- }
-
- public Task> GetPossibleParameters()
- {
- return Task.FromResult>(null);
- }
-
- public int? Progress()
- {
- return null;
- }
- }
-}
\ No newline at end of file
+// using System;
+// using System.Collections.Generic;
+// using System.Linq;
+// using System.Threading;
+// using System.Threading.Tasks;
+// using IdentityServer4.EntityFramework.DbContexts;
+// using IdentityServer4.EntityFramework.Mappers;
+// using IdentityServer4.Models;
+// using Kyoo.Models;
+// using Microsoft.EntityFrameworkCore;
+// using Microsoft.Extensions.DependencyInjection;
+//
+// namespace Kyoo.Tasks
+// {
+// public class CreateDatabase : ITask
+// {
+// public string Slug => "create-database";
+// public string Name => "Create the database";
+// public string Description => "Create the database if it does not exit and initialize it with defaults value.";
+// public string HelpMessage => null;
+// public bool RunOnStartup => true;
+// public int Priority => int.MaxValue;
+//
+// public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
+// {
+// using IServiceScope serviceScope = serviceProvider.CreateScope();
+// DatabaseContext databaseContext = serviceScope.ServiceProvider.GetService();
+// IdentityDatabase identityDatabase = serviceScope.ServiceProvider.GetService();
+// ConfigurationDbContext identityContext = serviceScope.ServiceProvider.GetService();
+//
+// databaseContext!.Database.Migrate();
+// identityDatabase!.Database.Migrate();
+// identityContext!.Database.Migrate();
+//
+// if (!identityContext.Clients.Any())
+// {
+// foreach (Client client in IdentityContext.GetClients())
+// identityContext.Clients.Add(client.ToEntity());
+// identityContext.SaveChanges();
+// }
+// if (!identityContext.IdentityResources.Any())
+// {
+// foreach (IdentityResource resource in IdentityContext.GetIdentityResources())
+// identityContext.IdentityResources.Add(resource.ToEntity());
+// identityContext.SaveChanges();
+// }
+// if (!identityContext.ApiResources.Any())
+// {
+// foreach (ApiResource resource in IdentityContext.GetApis())
+// identityContext.ApiResources.Add(resource.ToEntity());
+// identityContext.SaveChanges();
+// }
+// return Task.CompletedTask;
+// }
+//
+// public Task> GetPossibleParameters()
+// {
+// return Task.FromResult>(null);
+// }
+//
+// public int? Progress()
+// {
+// return null;
+// }
+// }
+// }
\ No newline at end of file
diff --git a/Kyoo/Tasks/ExtractMetadata.cs b/Kyoo/Tasks/ExtractMetadata.cs
index d5513c97..d3e339cb 100644
--- a/Kyoo/Tasks/ExtractMetadata.cs
+++ b/Kyoo/Tasks/ExtractMetadata.cs
@@ -1,120 +1,120 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Kyoo.Controllers;
-using Kyoo.Models;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Kyoo.Tasks
-{
- public class ExtractMetadata : ITask
- {
- public string Slug => "extract";
- public string Name => "Metadata Extractor";
- public string Description => "Extract subtitles or download thumbnails for a show/episode.";
- public string HelpMessage => null;
- public bool RunOnStartup => false;
- public int Priority => 0;
-
-
- private ILibraryManager _library;
- private IThumbnailsManager _thumbnails;
- private ITranscoder _transcoder;
-
- public async Task Run(IServiceProvider serviceProvider, CancellationToken token, string arguments = null)
- {
- string[] args = arguments?.Split('/');
-
- if (args == null || args.Length < 2)
- return;
-
- string slug = args[1];
- bool thumbs = args.Length < 3 || string.Equals(args[2], "thumbnails", StringComparison.InvariantCultureIgnoreCase);
- bool subs = args.Length < 3 || string.Equals(args[2], "subs", StringComparison.InvariantCultureIgnoreCase);
-
- using IServiceScope serviceScope = serviceProvider.CreateScope();
- _library = serviceScope.ServiceProvider.GetService();
- _thumbnails = serviceScope.ServiceProvider.GetService();
- _transcoder = serviceScope.ServiceProvider.GetService();
- int id;
-
- switch (args[0].ToLowerInvariant())
- {
- case "show":
- case "shows":
- Show show = await (int.TryParse(slug, out id)
- ? _library!.Get(id)
- : _library!.Get(slug));
- await ExtractShow(show, thumbs, subs, token);
- break;
- case "season":
- case "seasons":
- Season season = await (int.TryParse(slug, out id)
- ? _library!.Get(id)
- : _library!.Get(slug));
- await ExtractSeason(season, thumbs, subs, token);
- break;
- case "episode":
- case "episodes":
- Episode episode = await (int.TryParse(slug, out id)
- ? _library!.Get(id)
- : _library!.Get(slug));
- await ExtractEpisode(episode, thumbs, subs);
- break;
- }
- }
-
- private async Task ExtractShow(Show show, bool thumbs, bool subs, CancellationToken token)
- {
- if (thumbs)
- await _thumbnails!.Validate(show, true);
- await _library.Load(show, x => x.Seasons);
- foreach (Season season in show.Seasons)
- {
- if (token.IsCancellationRequested)
- return;
- await ExtractSeason(season, thumbs, subs, token);
- }
- }
-
- private async Task ExtractSeason(Season season, bool thumbs, bool subs, CancellationToken token)
- {
- if (thumbs)
- await _thumbnails!.Validate(season, true);
- await _library.Load(season, x => x.Episodes);
- foreach (Episode episode in season.Episodes)
- {
- if (token.IsCancellationRequested)
- return;
- await ExtractEpisode(episode, thumbs, subs);
- }
- }
-
- private async Task ExtractEpisode(Episode episode, bool thumbs, bool subs)
- {
- if (thumbs)
- await _thumbnails!.Validate(episode, true);
- if (subs)
- {
- await _library.Load(episode, x => x.Tracks);
- episode.Tracks = (await _transcoder!.ExtractInfos(episode, true))
- .Where(x => x.Type != StreamType.Attachment)
- .Concat(episode.Tracks.Where(x => x.IsExternal))
- .ToList();
- await _library.Edit(episode, false);
- }
- }
-
- public Task> GetPossibleParameters()
- {
- return Task.FromResult>(null);
- }
-
- public int? Progress()
- {
- return null;
- }
- }
-}
\ No newline at end of file
+// using System;
+// using System.Collections.Generic;
+// using System.Linq;
+// using System.Threading;
+// using System.Threading.Tasks;
+// using Kyoo.Controllers;
+// using Kyoo.Models;
+// using Microsoft.Extensions.DependencyInjection;
+//
+// namespace Kyoo.Tasks
+// {
+// public class ExtractMetadata : ITask
+// {
+// public string Slug => "extract";
+// public string Name => "Metadata Extractor";
+// public string Description => "Extract subtitles or download thumbnails for a show/episode.";
+// public string HelpMessage => null;
+// public bool RunOnStartup => false;
+// public int Priority => 0;
+//
+//
+// private ILibraryManager _library;
+// private IThumbnailsManager _thumbnails;
+// private ITranscoder _transcoder;
+//
+// public async Task Run(IServiceProvider serviceProvider, CancellationToken token, string arguments = null)
+// {
+// string[] args = arguments?.Split('/');
+//
+// if (args == null || args.Length < 2)
+// return;
+//
+// string slug = args[1];
+// bool thumbs = args.Length < 3 || string.Equals(args[2], "thumbnails", StringComparison.InvariantCultureIgnoreCase);
+// bool subs = args.Length < 3 || string.Equals(args[2], "subs", StringComparison.InvariantCultureIgnoreCase);
+//
+// using IServiceScope serviceScope = serviceProvider.CreateScope();
+// _library = serviceScope.ServiceProvider.GetService();
+// _thumbnails = serviceScope.ServiceProvider.GetService();
+// _transcoder = serviceScope.ServiceProvider.GetService();
+// int id;
+//
+// switch (args[0].ToLowerInvariant())
+// {
+// case "show":
+// case "shows":
+// Show show = await (int.TryParse(slug, out id)
+// ? _library!.Get(id)
+// : _library!.Get(slug));
+// await ExtractShow(show, thumbs, subs, token);
+// break;
+// case "season":
+// case "seasons":
+// Season season = await (int.TryParse(slug, out id)
+// ? _library!.Get(id)
+// : _library!.Get(slug));
+// await ExtractSeason(season, thumbs, subs, token);
+// break;
+// case "episode":
+// case "episodes":
+// Episode episode = await (int.TryParse(slug, out id)
+// ? _library!.Get(id)
+// : _library!.Get(slug));
+// await ExtractEpisode(episode, thumbs, subs);
+// break;
+// }
+// }
+//
+// private async Task ExtractShow(Show show, bool thumbs, bool subs, CancellationToken token)
+// {
+// if (thumbs)
+// await _thumbnails!.Validate(show, true);
+// await _library.Load(show, x => x.Seasons);
+// foreach (Season season in show.Seasons)
+// {
+// if (token.IsCancellationRequested)
+// return;
+// await ExtractSeason(season, thumbs, subs, token);
+// }
+// }
+//
+// private async Task ExtractSeason(Season season, bool thumbs, bool subs, CancellationToken token)
+// {
+// if (thumbs)
+// await _thumbnails!.Validate(season, true);
+// await _library.Load(season, x => x.Episodes);
+// foreach (Episode episode in season.Episodes)
+// {
+// if (token.IsCancellationRequested)
+// return;
+// await ExtractEpisode(episode, thumbs, subs);
+// }
+// }
+//
+// private async Task ExtractEpisode(Episode episode, bool thumbs, bool subs)
+// {
+// if (thumbs)
+// await _thumbnails!.Validate(episode, true);
+// if (subs)
+// {
+// await _library.Load(episode, x => x.Tracks);
+// episode.Tracks = (await _transcoder!.ExtractInfos(episode, true))
+// .Where(x => x.Type != StreamType.Attachment)
+// .Concat(episode.Tracks.Where(x => x.IsExternal))
+// .ToList();
+// await _library.Edit(episode, false);
+// }
+// }
+//
+// public Task> GetPossibleParameters()
+// {
+// return Task.FromResult>(null);
+// }
+//
+// public int? Progress()
+// {
+// return null;
+// }
+// }
+// }
\ No newline at end of file
diff --git a/Kyoo/Tasks/MetadataProviderLoader.cs b/Kyoo/Tasks/MetadataProviderLoader.cs
index 5811775e..899a2657 100644
--- a/Kyoo/Tasks/MetadataProviderLoader.cs
+++ b/Kyoo/Tasks/MetadataProviderLoader.cs
@@ -1,46 +1,46 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Kyoo.Controllers;
-using Kyoo.Models;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Kyoo.Tasks
-{
- public class MetadataProviderLoader : ITask
- {
- public string Slug => "reload-metdata";
- public string Name => "Reload Metadata Providers";
- public string Description => "Add every loaded metadata provider to the database.";
- public string HelpMessage => null;
- public bool RunOnStartup => true;
- public int Priority => 1000;
-
- public async Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
- {
- using IServiceScope serviceScope = serviceProvider.CreateScope();
- IProviderRepository providers = serviceScope.ServiceProvider.GetService();
- IThumbnailsManager thumbnails = serviceScope.ServiceProvider.GetService();
- IPluginManager pluginManager = serviceScope.ServiceProvider.GetService();
-
- foreach (IMetadataProvider provider in pluginManager!.GetPlugins())
- {
- if (string.IsNullOrEmpty(provider.Provider.Slug))
- throw new ArgumentException($"Empty provider slug (name: {provider.Provider.Name}).");
- await providers!.CreateIfNotExists(provider.Provider);
- await thumbnails!.Validate(provider.Provider);
- }
- }
-
- public Task> GetPossibleParameters()
- {
- return Task.FromResult>(null);
- }
-
- public int? Progress()
- {
- return null;
- }
- }
-}
\ No newline at end of file
+// using System;
+// using System.Collections.Generic;
+// using System.Threading;
+// using System.Threading.Tasks;
+// using Kyoo.Controllers;
+// using Kyoo.Models;
+// using Microsoft.Extensions.DependencyInjection;
+//
+// namespace Kyoo.Tasks
+// {
+// public class MetadataProviderLoader : ITask
+// {
+// public string Slug => "reload-metdata";
+// public string Name => "Reload Metadata Providers";
+// public string Description => "Add every loaded metadata provider to the database.";
+// public string HelpMessage => null;
+// public bool RunOnStartup => true;
+// public int Priority => 1000;
+//
+// public async Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
+// {
+// using IServiceScope serviceScope = serviceProvider.CreateScope();
+// IProviderRepository providers = serviceScope.ServiceProvider.GetService();
+// IThumbnailsManager thumbnails = serviceScope.ServiceProvider.GetService();
+// IPluginManager pluginManager = serviceScope.ServiceProvider.GetService();
+//
+// foreach (IMetadataProvider provider in pluginManager!.GetPlugins())
+// {
+// if (string.IsNullOrEmpty(provider.Provider.Slug))
+// throw new ArgumentException($"Empty provider slug (name: {provider.Provider.Name}).");
+// await providers!.CreateIfNotExists(provider.Provider);
+// await thumbnails!.Validate(provider.Provider);
+// }
+// }
+//
+// public Task> GetPossibleParameters()
+// {
+// return Task.FromResult>(null);
+// }
+//
+// public int? Progress()
+// {
+// return null;
+// }
+// }
+// }
\ No newline at end of file
diff --git a/Kyoo/Tasks/PluginLoader.cs b/Kyoo/Tasks/PluginLoader.cs
index b3f1c064..839e2f1e 100644
--- a/Kyoo/Tasks/PluginLoader.cs
+++ b/Kyoo/Tasks/PluginLoader.cs
@@ -1,37 +1,37 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Kyoo.Controllers;
-using Kyoo.Models;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Kyoo.Tasks
-{
- public class PluginLoader : ITask
- {
- public string Slug => "reload-plugin";
- public string Name => "Reload plugins";
- public string Description => "Reload all plugins from the plugin folder.";
- public string HelpMessage => null;
- public bool RunOnStartup => true;
- public int Priority => Int32.MaxValue;
- public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
- {
- using IServiceScope serviceScope = serviceProvider.CreateScope();
- IPluginManager pluginManager = serviceScope.ServiceProvider.GetService();
- pluginManager.ReloadPlugins();
- return Task.CompletedTask;
- }
-
- public Task> GetPossibleParameters()
- {
- return Task.FromResult>(null);
- }
-
- public int? Progress()
- {
- return null;
- }
- }
-}
\ No newline at end of file
+// using System;
+// using System.Collections.Generic;
+// using System.Threading;
+// using System.Threading.Tasks;
+// using Kyoo.Controllers;
+// using Kyoo.Models;
+// using Microsoft.Extensions.DependencyInjection;
+//
+// namespace Kyoo.Tasks
+// {
+// public class PluginLoader : ITask
+// {
+// public string Slug => "reload-plugin";
+// public string Name => "Reload plugins";
+// public string Description => "Reload all plugins from the plugin folder.";
+// public string HelpMessage => null;
+// public bool RunOnStartup => true;
+// public int Priority => Int32.MaxValue;
+// public Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
+// {
+// using IServiceScope serviceScope = serviceProvider.CreateScope();
+// IPluginManager pluginManager = serviceScope.ServiceProvider.GetService();
+// pluginManager.ReloadPlugins();
+// return Task.CompletedTask;
+// }
+//
+// public Task> GetPossibleParameters()
+// {
+// return Task.FromResult>(null);
+// }
+//
+// public int? Progress()
+// {
+// return null;
+// }
+// }
+// }
\ No newline at end of file
diff --git a/Kyoo/Views/LibraryApi.cs b/Kyoo/Views/LibraryApi.cs
index 93081db6..4ec44ea1 100644
--- a/Kyoo/Views/LibraryApi.cs
+++ b/Kyoo/Views/LibraryApi.cs
@@ -32,7 +32,7 @@ namespace Kyoo.Api
{
ActionResult result = await base.Create(resource);
if (result.Value != null)
- _taskManager.StartTask("scan", result.Value.Slug);
+ _taskManager.StartTask("scan", new Dictionary {{"slug", result.Value.Slug}});
return result;
}
diff --git a/Kyoo/Views/TaskAPI.cs b/Kyoo/Views/TaskAPI.cs
deleted file mode 100644
index 8e531bf9..00000000
--- a/Kyoo/Views/TaskAPI.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Kyoo.Controllers;
-using Microsoft.AspNetCore.Authorization;
-
-namespace Kyoo.Api
-{
- [Route("api/[controller]")]
- [ApiController]
- public class TaskController : ControllerBase
- {
- private readonly ITaskManager _taskManager;
-
- public TaskController(ITaskManager taskManager)
- {
- _taskManager = taskManager;
- }
-
-
- [HttpGet("{taskSlug}/{*args}")]
- [HttpPut("{taskSlug}/{*args}")]
- [Authorize(Policy="Admin")]
- public IActionResult RunTask(string taskSlug, string args = null)
- {
- if (_taskManager.StartTask(taskSlug, args))
- return Ok();
- return NotFound();
- }
- }
-}
diff --git a/Kyoo/Views/TaskApi.cs b/Kyoo/Views/TaskApi.cs
new file mode 100644
index 00000000..56b37b3d
--- /dev/null
+++ b/Kyoo/Views/TaskApi.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc;
+using Kyoo.Controllers;
+using Kyoo.Models.Exceptions;
+using Microsoft.AspNetCore.Authorization;
+
+namespace Kyoo.Api
+{
+ [Route("api/task")]
+ [Route("api/tasks")]
+ [ApiController]
+ [Authorize(Policy="Admin")]
+ public class TaskApi : ControllerBase
+ {
+ private readonly ITaskManager _taskManager;
+
+ public TaskApi(ITaskManager taskManager)
+ {
+ _taskManager = taskManager;
+ }
+
+
+ [HttpGet]
+ public ActionResult> GetTasks()
+ {
+ return Ok(_taskManager.GetAllTasks());
+ }
+
+ [HttpGet("{taskSlug}")]
+ [HttpPut("{taskSlug}")]
+ public IActionResult RunTask(string taskSlug, [FromQuery] Dictionary args)
+ {
+ try
+ {
+ _taskManager.StartTask(taskSlug, args);
+ return Ok();
+ }
+ catch (ItemNotFound)
+ {
+ return NotFound();
+ }
+ catch (ArgumentException ex)
+ {
+ return BadRequest(new {Error = ex.Message});
+ }
+ }
+ }
+}
diff --git a/Kyoo/settings.json b/Kyoo/settings.json
index bdd2f362..ef2aeb46 100644
--- a/Kyoo/settings.json
+++ b/Kyoo/settings.json
@@ -17,10 +17,12 @@
"logLevel": {
"default": "Warning",
"Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Microsoft.Hosting.Lifetime": "Information",
+ "Kyoo": "Trace"
}
},
+
"parallelTasks": "1",
"scheduledTasks": {