mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Delete the task system
This commit is contained in:
parent
dca91feff8
commit
13d79c5338
@ -1,55 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An interface to identify episodes, shows and metadata based on the episode file.
|
|
||||||
/// </summary>
|
|
||||||
public interface IIdentifier
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Identify a path and return the parsed metadata.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the episode file to parse.</param>
|
|
||||||
/// <exception cref="IdentificationFailedException">
|
|
||||||
/// The identifier could not work for the given path.
|
|
||||||
/// </exception>
|
|
||||||
/// <returns>
|
|
||||||
/// A tuple of models representing parsed metadata.
|
|
||||||
/// If no metadata could be parsed for a type, null can be returned.
|
|
||||||
/// </returns>
|
|
||||||
Task<(Collection, Show, Season, Episode)> Identify(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Identify an external subtitle or track file from it's path and return the parsed metadata.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the external track file to parse.</param>
|
|
||||||
/// <exception cref="IdentificationFailedException">
|
|
||||||
/// The identifier could not work for the given path.
|
|
||||||
/// </exception>
|
|
||||||
/// <returns>
|
|
||||||
/// The metadata of the track identified.
|
|
||||||
/// </returns>
|
|
||||||
Task<Track> IdentifyTrack(string path);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An interface to automatically retrieve metadata from external providers.
|
|
||||||
/// </summary>
|
|
||||||
public interface IMetadataProvider
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="Provider"/> corresponding to this provider.
|
|
||||||
/// This allow to map metadata to a provider, keep metadata links and
|
|
||||||
/// know witch <see cref="IMetadataProvider"/> is used for a specific <see cref="Library"/>.
|
|
||||||
/// </summary>
|
|
||||||
Provider Provider { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return a new item with metadata from your provider.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">
|
|
||||||
/// The item to retrieve metadata from. Most of the time, only the name will be available but other
|
|
||||||
/// properties may be filed by other providers before a call to this method. This can allow you to identify
|
|
||||||
/// the collection on your provider.
|
|
||||||
/// </param>
|
|
||||||
/// <remarks>
|
|
||||||
/// You must not use metadata from the given <paramref name="item"/>.
|
|
||||||
/// Merging metadata is the job of Kyoo, a complex <typeparamref name="T"/> is given
|
|
||||||
/// to make a precise search and give you every available properties, not to discard properties.
|
|
||||||
/// </remarks>
|
|
||||||
/// <typeparam name="T">The type of resource to retrieve metadata for.</typeparam>
|
|
||||||
/// <returns>A new <typeparamref name="T"/> containing metadata from your provider or null</returns>
|
|
||||||
[ItemCanBeNull]
|
|
||||||
Task<T> Get<T>([NotNull] T item)
|
|
||||||
where T : class, IResource;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Search for a specific type of items with a given query.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="query">The search query to use.</param>
|
|
||||||
/// <typeparam name="T">The type of resource to search metadata for.</typeparam>
|
|
||||||
/// <returns>The list of items that could be found on this specific provider.</returns>
|
|
||||||
[ItemNotNull]
|
|
||||||
Task<ICollection<T>> Search<T>(string query)
|
|
||||||
where T : class, IResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A special <see cref="IMetadataProvider"/> that merge results.
|
|
||||||
/// This interface exists to specify witch provider to use but it can be used like any other metadata provider.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class AProviderComposite : IMetadataProvider
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
[ItemNotNull]
|
|
||||||
public abstract Task<T> Get<T>(T item)
|
|
||||||
where T : class, IResource;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public abstract Task<ICollection<T>> Search<T>(string query)
|
|
||||||
where T : class, IResource;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Since this is a composite and not a real provider, no metadata is available.
|
|
||||||
/// It is not meant to be stored or selected. This class will handle merge based on what is required.
|
|
||||||
/// </summary>
|
|
||||||
public Provider Provider => null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Select witch providers to use.
|
|
||||||
/// The <see cref="IMetadataProvider"/> associated with the given <see cref="Provider"/> will be used.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="providers">The list of providers to use</param>
|
|
||||||
public abstract void UseProviders(IEnumerable<Provider> providers);
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,32 +34,11 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||||
public interface IPlugin
|
public interface IPlugin
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A slug to identify this plugin in queries.
|
|
||||||
/// </summary>
|
|
||||||
string Slug { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the plugin
|
/// The name of the plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Name { get; }
|
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>
|
/// <summary>
|
||||||
/// A list of types that will be available via the IOptions interfaces and will be listed inside
|
/// A list of types that will be available via the IOptions interfaces and will be listed inside
|
||||||
/// an IConfiguration.
|
/// an IConfiguration.
|
||||||
@ -73,7 +52,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// An optional configuration step to allow a plugin to change asp net configurations.
|
/// An optional configuration step to allow a plugin to change asp net configurations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="SA"/>
|
/// <seealso cref="SA"/>
|
||||||
virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
|
IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A configure method that will be run on plugin's startup.
|
/// A configure method that will be run on plugin's startup.
|
||||||
@ -94,15 +73,5 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
{
|
{
|
||||||
// Skipped
|
// 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A common interface that tasks should implement.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The list of parameters
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// All parameters that this task as. Every one of them will be given to the run function with a value.
|
|
||||||
/// </returns>
|
|
||||||
public TaskParameters GetParameters();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start this task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="arguments">
|
|
||||||
/// The list of parameters.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="progress">
|
|
||||||
/// The progress reporter. Used to inform the sender the percentage of completion of this task
|
|
||||||
/// .</param>
|
|
||||||
/// <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>
|
|
||||||
/// <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.
|
|
||||||
/// This exception allow the task to display a failure message to the end user while others exceptions
|
|
||||||
/// will be displayed as unhandled exceptions and display a stack trace.
|
|
||||||
/// </exception>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
public Task Run([NotNull] TaskParameters arguments,
|
|
||||||
[NotNull] IProgress<float> progress,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A single task parameter. This struct contains metadata to display and utility functions to get them in the task.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This struct will be used to generate the swagger documentation of the task.</remarks>
|
|
||||||
public record TaskParameter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name of this parameter.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The description of this parameter.
|
|
||||||
/// </summary>
|
|
||||||
public string Description { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of this parameter.
|
|
||||||
/// </summary>
|
|
||||||
public Type Type { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is this parameter required or can it be ignored?
|
|
||||||
/// </summary>
|
|
||||||
public bool IsRequired { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default value of this object.
|
|
||||||
/// </summary>
|
|
||||||
public object DefaultValue { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value of the parameter.
|
|
||||||
/// </summary>
|
|
||||||
private object _Value { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new task parameter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the parameter</param>
|
|
||||||
/// <param name="description">The description of the parameter</param>
|
|
||||||
/// <typeparam name="T">The type of the parameter.</typeparam>
|
|
||||||
/// <returns>A new task parameter.</returns>
|
|
||||||
public static TaskParameter Create<T>(string name, string description)
|
|
||||||
{
|
|
||||||
return new TaskParameter
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Description = description,
|
|
||||||
Type = typeof(T)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new required task parameter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the parameter</param>
|
|
||||||
/// <param name="description">The description of the parameter</param>
|
|
||||||
/// <typeparam name="T">The type of the parameter.</typeparam>
|
|
||||||
/// <returns>A new task parameter.</returns>
|
|
||||||
public static TaskParameter CreateRequired<T>(string name, string description)
|
|
||||||
{
|
|
||||||
return new TaskParameter
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Description = description,
|
|
||||||
Type = typeof(T),
|
|
||||||
IsRequired = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a parameter's value to give to a task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the parameter</param>
|
|
||||||
/// <param name="value">The value of the parameter. It's type will be used as parameter's type.</param>
|
|
||||||
/// <typeparam name="T">The type of the parameter</typeparam>
|
|
||||||
/// <returns>A TaskParameter that can be used as value.</returns>
|
|
||||||
public static TaskParameter CreateValue<T>(string name, T value)
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Type = typeof(T),
|
|
||||||
_Value = value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a parameter's value for the current parameter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to use</param>
|
|
||||||
/// <returns>A new parameter's value for this current parameter</returns>
|
|
||||||
public TaskParameter CreateValue(object value)
|
|
||||||
{
|
|
||||||
return this with { _Value = value };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the value of this parameter. If the value is of the wrong type, it will be converted.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of this parameter</typeparam>
|
|
||||||
/// <returns>The value of this parameter.</returns>
|
|
||||||
public T As<T>()
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(object))
|
|
||||||
return (T)_Value;
|
|
||||||
|
|
||||||
if (_Value is IResource resource)
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(string))
|
|
||||||
return (T)(object)resource.Slug;
|
|
||||||
if (typeof(T) == typeof(int))
|
|
||||||
return (T)(object)resource.ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (T)Convert.ChangeType(_Value, typeof(T));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A parameters container implementing an indexer to allow simple usage of parameters.
|
|
||||||
/// </summary>
|
|
||||||
public class TaskParameters : List<TaskParameter>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An indexer that return the parameter with the specified name.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the task (case sensitive)</param>
|
|
||||||
public TaskParameter this[string name] => this.FirstOrDefault(x => x.Name == name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new, empty, <see cref="TaskParameters"/>
|
|
||||||
/// </summary>
|
|
||||||
public TaskParameters() { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a <see cref="TaskParameters"/> with an initial parameters content.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parameters">The list of parameters</param>
|
|
||||||
public TaskParameters(IEnumerable<TaskParameter> parameters)
|
|
||||||
{
|
|
||||||
AddRange(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Threading;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A service to handle long running tasks.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The concurrent number of running tasks is implementation dependent.</remarks>
|
|
||||||
public interface ITaskManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Start a new task (or queue it).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="taskSlug">
|
|
||||||
/// The slug of the task to run.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="progress">
|
|
||||||
/// A progress reporter to know the percentage of completion of the task.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="arguments">
|
|
||||||
/// A list of arguments to pass to the task. An automatic conversion will be made if arguments to not fit.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="cancellationToken">
|
|
||||||
/// A custom cancellation token for the task.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument
|
|
||||||
/// invalid.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ItemNotFoundException">
|
|
||||||
/// The task could not be found.
|
|
||||||
/// </exception>
|
|
||||||
void StartTask(string taskSlug,
|
|
||||||
[NotNull] IProgress<float> progress,
|
|
||||||
Dictionary<string, object> arguments = null,
|
|
||||||
CancellationToken? cancellationToken = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start a new task (or queue it).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="progress">
|
|
||||||
/// A progress reporter to know the percentage of completion of the task.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="arguments">
|
|
||||||
/// A list of arguments to pass to the task. An automatic conversion will be made if arguments to not fit.
|
|
||||||
/// </param>
|
|
||||||
/// <typeparam name="T">
|
|
||||||
/// The type of the task to start.
|
|
||||||
/// </typeparam>
|
|
||||||
/// <param name="cancellationToken">
|
|
||||||
/// A custom cancellation token for the task.
|
|
||||||
/// </param>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument
|
|
||||||
/// invalid.
|
|
||||||
/// </exception>
|
|
||||||
/// <exception cref="ItemNotFoundException">
|
|
||||||
/// The task could not be found.
|
|
||||||
/// </exception>
|
|
||||||
void StartTask<T>([NotNull] IProgress<float> progress,
|
|
||||||
Dictionary<string, object> arguments = null,
|
|
||||||
CancellationToken? cancellationToken = null)
|
|
||||||
where T : ITask;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get all currently running tasks
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A list of currently running tasks.</returns>
|
|
||||||
ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get all available tasks
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A list of every tasks that this instance know.</returns>
|
|
||||||
ICollection<TaskMetadataAttribute> GetAllTasks();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Models.Exceptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An exception raised when an <see cref="IIdentifier"/> failed.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class IdentificationFailedException : Exception
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="IdentificationFailedException"/> with a default message.
|
|
||||||
/// </summary>
|
|
||||||
public IdentificationFailedException()
|
|
||||||
: base("An identification failed.")
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="IdentificationFailedException"/> with a custom message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">The message to use.</param>
|
|
||||||
public IdentificationFailedException(string message)
|
|
||||||
: base(message)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The serialization constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Serialization infos</param>
|
|
||||||
/// <param name="context">The serialization context</param>
|
|
||||||
protected IdentificationFailedException(SerializationInfo info, StreamingContext context)
|
|
||||||
: base(info, context)
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions.Models.Exceptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An exception raised when an <see cref="ITask"/> failed.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class TaskFailedException : AggregateException
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="TaskFailedException"/> with a default message.
|
|
||||||
/// </summary>
|
|
||||||
public TaskFailedException()
|
|
||||||
: base("A task failed.")
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="TaskFailedException"/> with a custom message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">The message to use.</param>
|
|
||||||
public TaskFailedException(string message)
|
|
||||||
: base(message)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="TaskFailedException"/> wrapping another exception.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="exception">The exception to wrap.</param>
|
|
||||||
public TaskFailedException(Exception exception)
|
|
||||||
: base(exception)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The serialization constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Serialization infos</param>
|
|
||||||
/// <param name="context">The serialization context</param>
|
|
||||||
protected TaskFailedException(SerializationInfo info, StreamingContext context)
|
|
||||||
: base(info, context)
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,12 +16,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Autofac.Builder;
|
using Autofac.Builder;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
namespace Kyoo.Abstractions
|
namespace Kyoo.Abstractions
|
||||||
{
|
{
|
||||||
@ -30,32 +28,6 @@ namespace Kyoo.Abstractions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Module
|
public static class Module
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Register a new task to the container.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder">The container</param>
|
|
||||||
/// <typeparam name="T">The type of the task</typeparam>
|
|
||||||
/// <returns>The registration builder of this new task. That can be used to edit the registration.</returns>
|
|
||||||
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
|
|
||||||
RegisterTask<T>(this ContainerBuilder builder)
|
|
||||||
where T : class, ITask
|
|
||||||
{
|
|
||||||
return builder.RegisterType<T>().As<ITask>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register a new metadata provider to the container.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder">The container</param>
|
|
||||||
/// <typeparam name="T">The type of the task</typeparam>
|
|
||||||
/// <returns>The registration builder of this new provider. That can be used to edit the registration.</returns>
|
|
||||||
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
|
|
||||||
RegisterProvider<T>(this ContainerBuilder builder)
|
|
||||||
where T : class, IMetadataProvider
|
|
||||||
{
|
|
||||||
return builder.RegisterType<T>().As<IMetadataProvider>().InstancePerLifetimeScope();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a new repository to the container.
|
/// Register a new repository to the container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -35,15 +35,9 @@ namespace Kyoo.Authentication
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AuthenticationModule : IPlugin
|
public class AuthenticationModule : IPlugin
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public string Slug => "auth";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "Authentication";
|
public string Name => "Authentication";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Description => "Enable an authentication/permission system for Kyoo (via Jwt or ApiKeys).";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Dictionary<string, Type> Configuration => new()
|
public Dictionary<string, Type> Configuration => new()
|
||||||
{
|
{
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Utils;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A metadata provider composite that merge results from all available providers.
|
|
||||||
/// </summary>
|
|
||||||
public class ProviderComposite : AProviderComposite
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The list of metadata providers
|
|
||||||
/// </summary>
|
|
||||||
private readonly ICollection<IMetadataProvider> _providers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The logger used to print errors.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger<ProviderComposite> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of selected providers. If no provider has been selected, this is null.
|
|
||||||
/// </summary>
|
|
||||||
private ICollection<Provider> _selectedProviders;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="ProviderComposite"/> with a list of available providers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="providers">The list of providers to merge.</param>
|
|
||||||
/// <param name="logger">The logger used to print errors.</param>
|
|
||||||
public ProviderComposite(IEnumerable<IMetadataProvider> providers, ILogger<ProviderComposite> logger)
|
|
||||||
{
|
|
||||||
_providers = providers.ToArray();
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void UseProviders(IEnumerable<Provider> providers)
|
|
||||||
{
|
|
||||||
_selectedProviders = providers.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return the list of providers that should be used for queries.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The list of providers to use, respecting the <see cref="UseProviders"/>.</returns>
|
|
||||||
private IEnumerable<IMetadataProvider> _GetProviders()
|
|
||||||
{
|
|
||||||
return _selectedProviders?
|
|
||||||
.Select(x => _providers.FirstOrDefault(y => y.Provider.Slug == x.Slug))
|
|
||||||
.Where(x => x != null)
|
|
||||||
?? _providers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task<T> Get<T>(T item)
|
|
||||||
{
|
|
||||||
T ret = item;
|
|
||||||
|
|
||||||
foreach (IMetadataProvider provider in _GetProviders())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ret = Merger.Merge(ret, await provider.Get(ret));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "The provider {Provider} could not get a {Type}",
|
|
||||||
provider.Provider.Name, typeof(T).Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task<ICollection<T>> Search<T>(string query)
|
|
||||||
{
|
|
||||||
List<T> ret = new();
|
|
||||||
|
|
||||||
foreach (IMetadataProvider provider in _GetProviders())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ret.AddRange(await provider.Search<T>(query));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "The provider {Provider} could not search for {Type}",
|
|
||||||
provider.Provider.Name, typeof(T).Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,160 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
using Kyoo.Core.Models;
|
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Kyoo.Utils;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An identifier that use a regex to extract basics metadata.
|
|
||||||
/// </summary>
|
|
||||||
public class RegexIdentifier : IIdentifier
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The configuration of kyoo to retrieve the identifier regex.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IOptionsMonitor<MediaOptions> _configuration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The library manager used to retrieve libraries paths.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="RegexIdentifier"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">The regex patterns to use.</param>
|
|
||||||
/// <param name="libraryManager">The library manager used to retrieve libraries paths.</param>
|
|
||||||
public RegexIdentifier(IOptionsMonitor<MediaOptions> configuration, ILibraryManager libraryManager)
|
|
||||||
{
|
|
||||||
_configuration = configuration;
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve the relative path of an episode or subtitle.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The full path of the episode</param>
|
|
||||||
/// <returns>The path relative to the library root.</returns>
|
|
||||||
private async Task<string> _GetRelativePath(string path)
|
|
||||||
{
|
|
||||||
string libraryPath = (await _libraryManager.GetAll<Library>())
|
|
||||||
.SelectMany(x => x.Paths)
|
|
||||||
.Where(path.StartsWith)
|
|
||||||
.MaxBy(x => x.Length);
|
|
||||||
return path[(libraryPath?.Length ?? 0)..];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<(Collection, Show, Season, Episode)> Identify(string path)
|
|
||||||
{
|
|
||||||
string relativePath = await _GetRelativePath(path);
|
|
||||||
Match match = _configuration.CurrentValue.Regex
|
|
||||||
.Select(x => new Regex(x, RegexOptions.IgnoreCase | RegexOptions.Compiled))
|
|
||||||
.Select(x => x.Match(relativePath))
|
|
||||||
.FirstOrDefault(x => x.Success);
|
|
||||||
|
|
||||||
if (match == null)
|
|
||||||
throw new IdentificationFailedException($"The episode at {path} does not match the episode's regex.");
|
|
||||||
|
|
||||||
(Collection collection, Show show, Season season, Episode episode) ret = (
|
|
||||||
collection: new Collection
|
|
||||||
{
|
|
||||||
Slug = Utility.ToSlug(match.Groups["Collection"].Value),
|
|
||||||
Name = match.Groups["Collection"].Value
|
|
||||||
},
|
|
||||||
show: new Show
|
|
||||||
{
|
|
||||||
Slug = Utility.ToSlug(match.Groups["Show"].Value),
|
|
||||||
Title = match.Groups["Show"].Value,
|
|
||||||
Path = Path.GetDirectoryName(path),
|
|
||||||
StartAir = match.Groups["StartYear"].Success
|
|
||||||
? new DateTime(int.Parse(match.Groups["StartYear"].Value), 1, 1)
|
|
||||||
: null
|
|
||||||
},
|
|
||||||
season: null,
|
|
||||||
episode: new Episode
|
|
||||||
{
|
|
||||||
SeasonNumber = match.Groups["Season"].Success
|
|
||||||
? int.Parse(match.Groups["Season"].Value)
|
|
||||||
: null,
|
|
||||||
EpisodeNumber = match.Groups["Episode"].Success
|
|
||||||
? int.Parse(match.Groups["Episode"].Value)
|
|
||||||
: null,
|
|
||||||
AbsoluteNumber = match.Groups["Absolute"].Success
|
|
||||||
? int.Parse(match.Groups["Absolute"].Value)
|
|
||||||
: null,
|
|
||||||
Path = path
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ret.episode.SeasonNumber.HasValue)
|
|
||||||
ret.season = new Season { SeasonNumber = ret.episode.SeasonNumber.Value };
|
|
||||||
|
|
||||||
if (ret.episode.SeasonNumber == null && ret.episode.EpisodeNumber == null
|
|
||||||
&& ret.episode.AbsoluteNumber == null)
|
|
||||||
{
|
|
||||||
ret.show.IsMovie = true;
|
|
||||||
ret.episode.Title = ret.show.Title;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<Track> IdentifyTrack(string path)
|
|
||||||
{
|
|
||||||
Match match = _configuration.CurrentValue.SubtitleRegex
|
|
||||||
.Select(x => new Regex(x, RegexOptions.IgnoreCase | RegexOptions.Compiled))
|
|
||||||
.Select(x => x.Match(path))
|
|
||||||
.FirstOrDefault(x => x.Success);
|
|
||||||
|
|
||||||
if (match == null)
|
|
||||||
throw new IdentificationFailedException($"The subtitle at {path} does not match the subtitle's regex.");
|
|
||||||
|
|
||||||
string episodePath = match.Groups["Episode"].Value;
|
|
||||||
string extension = Path.GetExtension(path);
|
|
||||||
return Task.FromResult(new Track
|
|
||||||
{
|
|
||||||
Type = StreamType.Subtitle,
|
|
||||||
Language = match.Groups["Language"].Value,
|
|
||||||
IsDefault = match.Groups["Default"].Success,
|
|
||||||
IsForced = match.Groups["Forced"].Success,
|
|
||||||
Codec = FileExtensions.SubtitleExtensions.GetValueOrDefault(extension, extension[1..]),
|
|
||||||
IsExternal = true,
|
|
||||||
Path = path,
|
|
||||||
Episode = new Episode
|
|
||||||
{
|
|
||||||
Path = episodePath
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,15 +24,12 @@ using Kyoo.Abstractions;
|
|||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Controllers;
|
using Kyoo.Core.Controllers;
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Kyoo.Core.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using IMetadataProvider = Kyoo.Abstractions.Controllers.IMetadataProvider;
|
|
||||||
using JsonOptions = Kyoo.Core.Api.JsonOptions;
|
using JsonOptions = Kyoo.Core.Api.JsonOptions;
|
||||||
|
|
||||||
namespace Kyoo.Core
|
namespace Kyoo.Core
|
||||||
@ -42,22 +39,13 @@ namespace Kyoo.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class CoreModule : IPlugin
|
public class CoreModule : IPlugin
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public string Slug => "core";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "Core";
|
public string Name => "Core";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Description => "The core module containing default implementations.";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Dictionary<string, Type> Configuration => new()
|
public Dictionary<string, Type> Configuration => new()
|
||||||
{
|
{
|
||||||
{ TaskOptions.Path, typeof(TaskOptions) },
|
|
||||||
{ MediaOptions.Path, typeof(MediaOptions) },
|
|
||||||
{ "database", null },
|
{ "database", null },
|
||||||
{ "logging", null }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -69,17 +57,6 @@ namespace Kyoo.Core
|
|||||||
builder.RegisterType<Transcoder>().As<ITranscoder>().SingleInstance();
|
builder.RegisterType<Transcoder>().As<ITranscoder>().SingleInstance();
|
||||||
builder.RegisterType<ThumbnailsManager>().As<IThumbnailsManager>().InstancePerLifetimeScope();
|
builder.RegisterType<ThumbnailsManager>().As<IThumbnailsManager>().InstancePerLifetimeScope();
|
||||||
builder.RegisterType<LibraryManager>().As<ILibraryManager>().InstancePerLifetimeScope();
|
builder.RegisterType<LibraryManager>().As<ILibraryManager>().InstancePerLifetimeScope();
|
||||||
builder.RegisterType<RegexIdentifier>().As<IIdentifier>().SingleInstance();
|
|
||||||
|
|
||||||
builder.RegisterComposite<ProviderComposite, IMetadataProvider>();
|
|
||||||
builder.Register(x => (AProviderComposite)x.Resolve<IMetadataProvider>());
|
|
||||||
|
|
||||||
builder.RegisterTask<Crawler>();
|
|
||||||
builder.RegisterTask<Housekeeping>();
|
|
||||||
builder.RegisterTask<RegisterEpisode>();
|
|
||||||
builder.RegisterTask<RegisterSubtitle>();
|
|
||||||
builder.RegisterTask<MetadataProviderLoader>();
|
|
||||||
builder.RegisterTask<LibraryCreator>();
|
|
||||||
|
|
||||||
builder.RegisterRepository<ILibraryRepository, LibraryRepository>();
|
builder.RegisterRepository<ILibraryRepository, LibraryRepository>();
|
||||||
builder.RegisterRepository<ILibraryItemRepository, LibraryItemRepository>();
|
builder.RegisterRepository<ILibraryItemRepository, LibraryItemRepository>();
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Models
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A static class allowing one to identify files extensions.
|
|
||||||
/// </summary>
|
|
||||||
public static class FileExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The list of known video extensions
|
|
||||||
/// </summary>
|
|
||||||
public static readonly ImmutableArray<string> VideoExtensions = ImmutableArray.Create(
|
|
||||||
".webm",
|
|
||||||
".mkv",
|
|
||||||
".flv",
|
|
||||||
".vob",
|
|
||||||
".ogg",
|
|
||||||
".ogv",
|
|
||||||
".avi",
|
|
||||||
".mts",
|
|
||||||
".m2ts",
|
|
||||||
".ts",
|
|
||||||
".mov",
|
|
||||||
".qt",
|
|
||||||
".asf",
|
|
||||||
".mp4",
|
|
||||||
".m4p",
|
|
||||||
".m4v",
|
|
||||||
".mpg",
|
|
||||||
".mp2",
|
|
||||||
".mpeg",
|
|
||||||
".mpe",
|
|
||||||
".mpv",
|
|
||||||
".m2v",
|
|
||||||
".3gp",
|
|
||||||
".3g2"
|
|
||||||
);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if a file represent a video file (only by checking the extension of the file)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filePath">The path of the file to check</param>
|
|
||||||
/// <returns><c>true</c> if the file is a video file, <c>false</c> otherwise.</returns>
|
|
||||||
public static bool IsVideo(string filePath)
|
|
||||||
{
|
|
||||||
return VideoExtensions.Contains(Path.GetExtension(filePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The dictionary of known subtitles extensions and the name of the subtitle codec.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly ImmutableDictionary<string, string> SubtitleExtensions = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ ".ass", "ass" },
|
|
||||||
{ ".str", "subrip" }
|
|
||||||
}.ToImmutableDictionary();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if a file represent a subtitle file (only by checking the extension of the file)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filePath">The path of the file to check</param>
|
|
||||||
/// <returns><c>true</c> if the file is a subtitle file, <c>false</c> otherwise.</returns>
|
|
||||||
public static bool IsSubtitle(string filePath)
|
|
||||||
{
|
|
||||||
return SubtitleExtensions.ContainsKey(Path.GetExtension(filePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,11 +28,6 @@ namespace Kyoo.Core.Models.Options
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Path = "Basics";
|
public const string Path = "Basics";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The path of the plugin directory.
|
|
||||||
/// </summary>
|
|
||||||
public string PluginPath { get; set; } = "plugins/";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The temporary folder to cache transmuxed file.
|
/// The temporary folder to cache transmuxed file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Models.Options
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Options for media registering.
|
|
||||||
/// </summary>
|
|
||||||
public class MediaOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The path of this options
|
|
||||||
/// </summary>
|
|
||||||
public const string Path = "Media";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A regex for episodes
|
|
||||||
/// </summary>
|
|
||||||
public string[] Regex { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A regex for subtitles
|
|
||||||
/// </summary>
|
|
||||||
public string[] SubtitleRegex { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Models.Options
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Options related to tasks
|
|
||||||
/// </summary>
|
|
||||||
public class TaskOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The path of this options
|
|
||||||
/// </summary>
|
|
||||||
public const string Path = "Tasks";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of tasks that can be run concurrently.
|
|
||||||
/// </summary>
|
|
||||||
public int Parallels { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The delay of tasks that should be automatically started at fixed times.
|
|
||||||
/// </summary>
|
|
||||||
[UsedImplicitly]
|
|
||||||
public Dictionary<string, TimeSpan> Scheduled { get; set; } = new();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Core.Models;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task to add new video files.
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("scan", "Scan libraries", "Scan your libraries and load data for new shows.", RunOnStartup = true)]
|
|
||||||
public class Crawler : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The library manager used to get libraries and providers to use.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The file manager used walk inside directories and check they existences.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A task manager used to create sub tasks for each episode to add to the database.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ITaskManager _taskManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The logger used to inform the current status to the console.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger<Crawler> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="Crawler"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">The library manager to retrieve existing episodes/library/tracks</param>
|
|
||||||
/// <param name="fileSystem">The file system to glob files</param>
|
|
||||||
/// <param name="taskManager">The task manager used to start <see cref="RegisterEpisode"/>.</param>
|
|
||||||
/// <param name="logger">The logger used print messages.</param>
|
|
||||||
public Crawler(ILibraryManager libraryManager,
|
|
||||||
IFileSystem fileSystem,
|
|
||||||
ITaskManager taskManager,
|
|
||||||
ILogger<Crawler> logger)
|
|
||||||
{
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_taskManager = taskManager;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
TaskParameter.Create<string>("slug", "A library slug to restrict the scan to this library.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
string argument = arguments["slug"].As<string>();
|
|
||||||
ICollection<Library> libraries = argument == null
|
|
||||||
? await _libraryManager.GetAll<Library>()
|
|
||||||
: new[] { await _libraryManager.GetOrDefault<Library>(argument) };
|
|
||||||
|
|
||||||
if (argument != null && libraries.First() == null)
|
|
||||||
throw new ArgumentException($"No library found with the name {argument}");
|
|
||||||
|
|
||||||
foreach (Library library in libraries)
|
|
||||||
await _libraryManager.Load(library, x => x.Providers);
|
|
||||||
|
|
||||||
progress.Report(0);
|
|
||||||
float percent = 0;
|
|
||||||
|
|
||||||
ICollection<Episode> episodes = await _libraryManager.GetAll<Episode>();
|
|
||||||
ICollection<Track> tracks = await _libraryManager.GetAll<Track>();
|
|
||||||
foreach (Library library in libraries)
|
|
||||||
{
|
|
||||||
IProgress<float> reporter = new Progress<float>(x =>
|
|
||||||
{
|
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
|
||||||
progress.Report(percent + (x / libraries.Count));
|
|
||||||
});
|
|
||||||
await _Scan(library, episodes, tracks, reporter, cancellationToken);
|
|
||||||
percent += 100f / libraries.Count;
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task _Scan(Library library,
|
|
||||||
IEnumerable<Episode> episodes,
|
|
||||||
IEnumerable<Track> tracks,
|
|
||||||
IProgress<float> progress,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Scanning library {Library} at {Paths}", library.Name, library.Paths);
|
|
||||||
foreach (string path in library.Paths)
|
|
||||||
{
|
|
||||||
ICollection<string> files = await _fileSystem.ListFiles(path, SearchOption.AllDirectories);
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// We try to group episodes by shows to register one episode of each show first.
|
|
||||||
// This speeds up the scan process because further episodes of a show are registered when all metadata
|
|
||||||
// of the show has already been fetched.
|
|
||||||
List<IGrouping<string, string>> shows = files
|
|
||||||
.Where(FileExtensions.IsVideo)
|
|
||||||
.Where(x => !Path.GetFileName(x).StartsWith('.')) // ignore hidden files.
|
|
||||||
.Where(x => episodes.All(y => y.Path != x))
|
|
||||||
.GroupBy(Path.GetDirectoryName)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
string[] paths = shows.Select(x => x.First())
|
|
||||||
.Concat(shows.SelectMany(x => x.Skip(1)))
|
|
||||||
.ToArray();
|
|
||||||
float percent = 0;
|
|
||||||
IProgress<float> reporter = new Progress<float>(x =>
|
|
||||||
{
|
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
|
||||||
progress.Report((percent + (x / paths.Length) - 10) / library.Paths.Length);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (string episodePath in paths)
|
|
||||||
{
|
|
||||||
_taskManager.StartTask<RegisterEpisode>(reporter, new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
["path"] = episodePath,
|
|
||||||
["library"] = library
|
|
||||||
}, cancellationToken);
|
|
||||||
percent += 100f / paths.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] subtitles = files
|
|
||||||
.Where(FileExtensions.IsSubtitle)
|
|
||||||
.Where(x => !x.Contains("Extra"))
|
|
||||||
.Where(x => tracks.All(y => y.Path != x))
|
|
||||||
.ToArray();
|
|
||||||
percent = 0;
|
|
||||||
reporter = new Progress<float>(x =>
|
|
||||||
{
|
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
|
||||||
progress.Report((90 + (percent + (x / subtitles.Length))) / library.Paths.Length);
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (string trackPath in subtitles)
|
|
||||||
{
|
|
||||||
_taskManager.StartTask<RegisterSubtitle>(reporter, new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
["path"] = trackPath
|
|
||||||
}, cancellationToken);
|
|
||||||
percent += 100f / subtitles.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// 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<ILibraryManager>();
|
|
||||||
// _thumbnails = serviceScope.ServiceProvider.GetService<IThumbnailsManager>();
|
|
||||||
// _transcoder = serviceScope.ServiceProvider.GetService<ITranscoder>();
|
|
||||||
// int id;
|
|
||||||
//
|
|
||||||
// switch (args[0].ToLowerInvariant())
|
|
||||||
// {
|
|
||||||
// case "show":
|
|
||||||
// case "shows":
|
|
||||||
// Show show = await (int.TryParse(slug, out id)
|
|
||||||
// ? _library!.Get<Show>(id)
|
|
||||||
// : _library!.Get<Show>(slug));
|
|
||||||
// await ExtractShow(show, thumbs, subs, token);
|
|
||||||
// break;
|
|
||||||
// case "season":
|
|
||||||
// case "seasons":
|
|
||||||
// Season season = await (int.TryParse(slug, out id)
|
|
||||||
// ? _library!.Get<Season>(id)
|
|
||||||
// : _library!.Get<Season>(slug));
|
|
||||||
// await ExtractSeason(season, thumbs, subs, token);
|
|
||||||
// break;
|
|
||||||
// case "episode":
|
|
||||||
// case "episodes":
|
|
||||||
// Episode episode = await (int.TryParse(slug, out id)
|
|
||||||
// ? _library!.Get<Episode>(id)
|
|
||||||
// : _library!.Get<Episode>(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<IEnumerable<string>> GetPossibleParameters()
|
|
||||||
// {
|
|
||||||
// return Task.FromResult<IEnumerable<string>>(null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public int? Progress()
|
|
||||||
// {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -1,103 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task to remove orphaned episode and series.
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("housekeeping", "Housekeeping", "Remove orphaned episode and series.", RunOnStartup = true)]
|
|
||||||
public class Housekeeping : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The library manager used to get libraries or remove deleted episodes.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The file manager used walk inside directories and check they existences.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The logger used to inform the user that episodes has been removed.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger<Housekeeping> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="Housekeeping"/> task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">The library manager used to get libraries or remove deleted episodes.</param>
|
|
||||||
/// <param name="fileSystem">The file manager used walk inside directories and check they existences.</param>
|
|
||||||
/// <param name="logger">The logger used to inform the user that episodes has been removed.</param>
|
|
||||||
public Housekeeping(ILibraryManager libraryManager, IFileSystem fileSystem, ILogger<Housekeeping> logger)
|
|
||||||
{
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
int delCount = await _libraryManager.GetCount<Show>() + await _libraryManager.GetCount<Episode>();
|
|
||||||
progress.Report(0);
|
|
||||||
|
|
||||||
foreach (Show show in await _libraryManager.GetAll<Show>())
|
|
||||||
{
|
|
||||||
progress.Report(count / delCount * 100);
|
|
||||||
count++;
|
|
||||||
|
|
||||||
if (await _fileSystem.Exists(show.Path))
|
|
||||||
continue;
|
|
||||||
_logger.LogWarning("Show {Name}'s folder has been deleted (was {Path}), removing it from kyoo",
|
|
||||||
show.Title, show.Path);
|
|
||||||
await _libraryManager.Delete(show);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Episode episode in await _libraryManager.GetAll<Episode>())
|
|
||||||
{
|
|
||||||
progress.Report(count / delCount * 100);
|
|
||||||
count++;
|
|
||||||
|
|
||||||
if (await _fileSystem.Exists(episode.Path))
|
|
||||||
continue;
|
|
||||||
_logger.LogWarning("Episode {Slug}'s file has been deleted (was {Path}), removing it from kyoo",
|
|
||||||
episode.Slug, episode.Path);
|
|
||||||
await _libraryManager.Delete(episode);
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Utils;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task to add new video files.
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("library-creator", "Create libraries", "Create libraries on the library root folder.",
|
|
||||||
RunOnStartup = true, Priority = 500)]
|
|
||||||
public class LibraryCreator : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The library manager used to get libraries and providers to use.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="Crawler"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="libraryManager">The library manager to retrieve existing episodes/library/tracks</param>
|
|
||||||
public LibraryCreator(ILibraryManager libraryManager)
|
|
||||||
{
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
ICollection<Provider> providers = await _libraryManager.GetAll<Provider>();
|
|
||||||
ICollection<string> existings = (await _libraryManager.GetAll<Library>()).SelectMany(x => x.Paths).ToArray();
|
|
||||||
IEnumerable<Library> newLibraries = Directory.GetDirectories(Environment.GetEnvironmentVariable("KYOO_LIBRARY_ROOT") ?? "/video")
|
|
||||||
.Where(x => !existings.Contains(x))
|
|
||||||
.Select(x => new Library
|
|
||||||
{
|
|
||||||
Slug = Utility.ToSlug(Path.GetFileName(x)),
|
|
||||||
Name = Path.GetFileName(x),
|
|
||||||
Paths = new string[] { x },
|
|
||||||
Providers = providers,
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (Library library in newLibraries)
|
|
||||||
{
|
|
||||||
await _libraryManager.Create(library);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task that download metadata providers images.
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("reload-metadata", "Reload Metadata Providers", "Add every loaded metadata provider to the database.",
|
|
||||||
RunOnStartup = true, Priority = 1000, IsHidden = true)]
|
|
||||||
public class MetadataProviderLoader : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The provider repository used to create in-db providers from metadata providers.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IProviderRepository _providers;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The thumbnail manager used to download providers logo.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IThumbnailsManager _thumbnails;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of metadata providers to register.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ICollection<IMetadataProvider> _metadataProviders;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="MetadataProviderLoader"/> task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="providers">
|
|
||||||
/// The provider repository used to create in-db providers from metadata providers.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="thumbnails">
|
|
||||||
/// The thumbnail manager used to download providers logo.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="metadataProviders">
|
|
||||||
/// The list of metadata providers to register.
|
|
||||||
/// </param>
|
|
||||||
public MetadataProviderLoader(IProviderRepository providers,
|
|
||||||
IThumbnailsManager thumbnails,
|
|
||||||
ICollection<IMetadataProvider> metadataProviders)
|
|
||||||
{
|
|
||||||
_providers = providers;
|
|
||||||
_thumbnails = thumbnails;
|
|
||||||
_metadataProviders = metadataProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new TaskParameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
float percent = 0;
|
|
||||||
progress.Report(0);
|
|
||||||
|
|
||||||
foreach (IMetadataProvider provider in _metadataProviders)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(provider.Provider.Slug))
|
|
||||||
throw new TaskFailedException($"Empty provider slug (name: {provider.Provider.Name}).");
|
|
||||||
await _providers.CreateIfNotExists(provider.Provider);
|
|
||||||
await _thumbnails.DownloadImages(provider.Provider);
|
|
||||||
percent += 100f / _metadataProviders.Count;
|
|
||||||
progress.Report(percent);
|
|
||||||
}
|
|
||||||
progress.Report(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// 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 ReScan: ITask
|
|
||||||
// {
|
|
||||||
// public string Slug => "re-scan";
|
|
||||||
// public string Name => "ReScan";
|
|
||||||
// public string Description => "Re download metadata of an item using it's external ids.";
|
|
||||||
// public string HelpMessage => null;
|
|
||||||
// public bool RunOnStartup => false;
|
|
||||||
// public int Priority => 0;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// private IServiceProvider _serviceProvider;
|
|
||||||
// private IThumbnailsManager _thumbnailsManager;
|
|
||||||
// private IProviderManager _providerManager;
|
|
||||||
// private DatabaseContext _database;
|
|
||||||
//
|
|
||||||
// public async Task Run(IServiceProvider serviceProvider, CancellationToken cancellationToken, string arguments = null)
|
|
||||||
// {
|
|
||||||
// using IServiceScope serviceScope = serviceProvider.CreateScope();
|
|
||||||
// _serviceProvider = serviceProvider;
|
|
||||||
// _thumbnailsManager = serviceProvider.GetService<IThumbnailsManager>();
|
|
||||||
// _providerManager = serviceProvider.GetService<IProviderManager>();
|
|
||||||
// _database = serviceScope.ServiceProvider.GetService<DatabaseContext>();
|
|
||||||
//
|
|
||||||
// if (arguments == null || !arguments.Contains('/'))
|
|
||||||
// return;
|
|
||||||
//
|
|
||||||
// string slug = arguments.Substring(arguments.IndexOf('/') + 1);
|
|
||||||
// switch (arguments.Substring(0, arguments.IndexOf('/')))
|
|
||||||
// {
|
|
||||||
// case "show":
|
|
||||||
// await ReScanShow(slug);
|
|
||||||
// break;
|
|
||||||
// case "season":
|
|
||||||
// await ReScanSeason(slug);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private async Task ReScanShow(string slug)
|
|
||||||
// {
|
|
||||||
// Show old;
|
|
||||||
//
|
|
||||||
// using (IServiceScope serviceScope = _serviceProvider.CreateScope())
|
|
||||||
// {
|
|
||||||
// ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
|
||||||
// old = _database.Shows.FirstOrDefault(x => x.Slug == slug);
|
|
||||||
// if (old == null)
|
|
||||||
// return;
|
|
||||||
// Library library = _database.LibraryLinks.First(x => x.Show == old && x.Library != null).Library;
|
|
||||||
// Show edited = await _providerManager.CompleteShow(old, library);
|
|
||||||
// edited.ID = old.ID;
|
|
||||||
// edited.Slug = old.Slug;
|
|
||||||
// edited.Path = old.Path;
|
|
||||||
// await libraryManager.EditShow(edited, true);
|
|
||||||
// await _thumbnailsManager.Validate(edited, true);
|
|
||||||
// }
|
|
||||||
// if (old.Seasons != null)
|
|
||||||
// await Task.WhenAll(old.Seasons.Select(x => ReScanSeason(old, x)));
|
|
||||||
// IEnumerable<Episode> orphans = old.Episodes.Where(x => x.Season == null).ToList();
|
|
||||||
// if (orphans.Any())
|
|
||||||
// await Task.WhenAll(orphans.Select(x => ReScanEpisode(old, x)));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private async Task ReScanSeason(string seasonSlug)
|
|
||||||
// {
|
|
||||||
// string[] infos = seasonSlug.Split('-');
|
|
||||||
// if (infos.Length != 2 || int.TryParse(infos[1], out int seasonNumber))
|
|
||||||
// return;
|
|
||||||
// string slug = infos[0];
|
|
||||||
// Show show = _database.Shows.FirstOrDefault(x => x.Slug == slug);
|
|
||||||
// if (show == null)
|
|
||||||
// return;
|
|
||||||
// Season old = _database.Seasons.FirstOrDefault(x => x.SeasonNumber == seasonNumber && x.Show.ID == show.ID);
|
|
||||||
// if (old == null)
|
|
||||||
// return;
|
|
||||||
// await ReScanSeason(show, old);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private async Task ReScanSeason(Show show, Season old)
|
|
||||||
// {
|
|
||||||
// using (IServiceScope serviceScope = _serviceProvider.CreateScope())
|
|
||||||
// {
|
|
||||||
// ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
|
||||||
// Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
|
|
||||||
// Season edited = await _providerManager.GetSeason(show, old.SeasonNumber, library);
|
|
||||||
// edited.ID = old.ID;
|
|
||||||
// await libraryManager.EditSeason(edited, true);
|
|
||||||
// await _thumbnailsManager.Validate(edited, true);
|
|
||||||
// }
|
|
||||||
// if (old.Episodes != null)
|
|
||||||
// await Task.WhenAll(old.Episodes.Select(x => ReScanEpisode(show, x)));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private async Task ReScanEpisode(Show show, Episode old)
|
|
||||||
// {
|
|
||||||
// using IServiceScope serviceScope = _serviceProvider.CreateScope();
|
|
||||||
// ILibraryManager libraryManager = serviceScope.ServiceProvider.GetService<ILibraryManager>();
|
|
||||||
//
|
|
||||||
// Library library = _database.LibraryLinks.First(x => x.Show == show && x.Library != null).Library;
|
|
||||||
// Episode edited = await _providerManager.GetEpisode(show, old.Path, old.SeasonNumber, old.EpisodeNumber, old.AbsoluteNumber, library);
|
|
||||||
// edited.ID = old.ID;
|
|
||||||
// await libraryManager.EditEpisode(edited, true);
|
|
||||||
// await _thumbnailsManager.Validate(edited, true);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public Task<IEnumerable<string>> GetPossibleParameters()
|
|
||||||
// {
|
|
||||||
// return Task.FromResult<IEnumerable<string>>(null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public int? Progress()
|
|
||||||
// {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -1,210 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task to register a new episode
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("register", "Register episode", "Register a new episode")]
|
|
||||||
public class RegisterEpisode : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An identifier to extract metadata from paths.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IIdentifier _identifier;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The library manager used to register the episode.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A metadata provider to retrieve the metadata of the new episode (and related items if they do not exist).
|
|
||||||
/// </summary>
|
|
||||||
private readonly AProviderComposite _metadataProvider;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The thumbnail manager used to download images.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IThumbnailsManager _thumbnailsManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The transcoder used to extract subtitles and metadata.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IFileSystem _transcoder;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="RegisterEpisode"/> task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="identifier">
|
|
||||||
/// An identifier to extract metadata from paths.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="libraryManager">
|
|
||||||
/// The library manager used to register the episode.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="metadataProvider">
|
|
||||||
/// A metadata provider to retrieve the metadata of the new episode (and related items if they do not exist).
|
|
||||||
/// </param>
|
|
||||||
/// <param name="thumbnailsManager">
|
|
||||||
/// The thumbnail manager used to download images.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="transcoder">
|
|
||||||
/// The file manager used to retrieve episodes metadata.
|
|
||||||
/// </param>
|
|
||||||
public RegisterEpisode(IIdentifier identifier,
|
|
||||||
ILibraryManager libraryManager,
|
|
||||||
AProviderComposite metadataProvider,
|
|
||||||
IThumbnailsManager thumbnailsManager,
|
|
||||||
IFileSystem transcoder)
|
|
||||||
{
|
|
||||||
_identifier = identifier;
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_metadataProvider = metadataProvider;
|
|
||||||
_thumbnailsManager = thumbnailsManager;
|
|
||||||
_transcoder = transcoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
TaskParameter.CreateRequired<string>("path", "The path of the episode file"),
|
|
||||||
TaskParameter.CreateRequired<Library>("library", "The library in witch the episode is")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
string path = arguments["path"].As<string>();
|
|
||||||
Library library = arguments["library"].As<Library>();
|
|
||||||
progress.Report(0);
|
|
||||||
|
|
||||||
if (library != null)
|
|
||||||
{
|
|
||||||
if (library.Providers == null)
|
|
||||||
await _libraryManager.Load(library, x => x.Providers);
|
|
||||||
_metadataProvider.UseProviders(library.Providers);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
(Collection collection, Show show, Season season, Episode episode) = await _identifier.Identify(path);
|
|
||||||
progress.Report(15);
|
|
||||||
|
|
||||||
collection = await _RegisterAndFill(collection);
|
|
||||||
progress.Report(20);
|
|
||||||
|
|
||||||
Show registeredShow = await _RegisterAndFill(show);
|
|
||||||
if (registeredShow.Path != show.Path)
|
|
||||||
{
|
|
||||||
if (show.StartAir.HasValue)
|
|
||||||
{
|
|
||||||
show.Slug += $"-{show.StartAir.Value.Year}";
|
|
||||||
show = await _libraryManager.Create(show);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new TaskFailedException($"Duplicated show found ({show.Slug}) " +
|
|
||||||
$"at {registeredShow.Path} and {show.Path}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
show = registeredShow;
|
|
||||||
|
|
||||||
progress.Report(50);
|
|
||||||
|
|
||||||
if (season != null)
|
|
||||||
season.Show = show;
|
|
||||||
season = await _RegisterAndFill(season);
|
|
||||||
progress.Report(60);
|
|
||||||
|
|
||||||
episode.Show = show;
|
|
||||||
episode.Season = season;
|
|
||||||
if (!show.IsMovie)
|
|
||||||
episode = await _metadataProvider.Get(episode);
|
|
||||||
progress.Report(70);
|
|
||||||
episode.Tracks = await _transcoder.ExtractInfos(episode, false);
|
|
||||||
await _thumbnailsManager.DownloadImages(episode);
|
|
||||||
progress.Report(90);
|
|
||||||
|
|
||||||
await _libraryManager.Create(episode);
|
|
||||||
progress.Report(95);
|
|
||||||
await _libraryManager.AddShowLink(show, library, collection);
|
|
||||||
progress.Report(100);
|
|
||||||
}
|
|
||||||
catch (IdentificationFailedException ex)
|
|
||||||
{
|
|
||||||
throw new TaskFailedException(ex);
|
|
||||||
}
|
|
||||||
catch (DuplicatedItemException ex)
|
|
||||||
{
|
|
||||||
throw new TaskFailedException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve the equivalent item if it already exists in the database,
|
|
||||||
/// if it does not, fill metadata using the metadata provider, download images and register the item to the
|
|
||||||
/// database.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item to retrieve or fill and register</param>
|
|
||||||
/// <typeparam name="T">The type of the item</typeparam>
|
|
||||||
/// <returns>The existing or filled item.</returns>
|
|
||||||
private async Task<T> _RegisterAndFill<T>(T item)
|
|
||||||
where T : class, IResource, IThumbnails, IMetadata
|
|
||||||
{
|
|
||||||
if (item == null || string.IsNullOrEmpty(item.Slug))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
T existing = await _libraryManager.GetOrDefault<T>(item.Slug);
|
|
||||||
if (existing != null)
|
|
||||||
{
|
|
||||||
await _libraryManager.Load(existing, x => x.ExternalIDs);
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = await _metadataProvider.Get(item);
|
|
||||||
await _thumbnailsManager.DownloadImages(item);
|
|
||||||
|
|
||||||
switch (item)
|
|
||||||
{
|
|
||||||
case Show show when show.People != null:
|
|
||||||
foreach (PeopleRole role in show.People)
|
|
||||||
await _thumbnailsManager.DownloadImages(role.People);
|
|
||||||
break;
|
|
||||||
case Season season:
|
|
||||||
season.Title ??= $"Season {season.SeasonNumber}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _libraryManager.CreateIfNotExists(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task to register a new episode
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("register-sub", "Register subtitle", "Register a new subtitle")]
|
|
||||||
public class RegisterSubtitle : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An identifier to extract metadata from paths.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IIdentifier _identifier;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The library manager used to register the episode.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="RegisterSubtitle"/> task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="identifier">An identifier to extract metadata from paths.</param>
|
|
||||||
/// <param name="libraryManager">The library manager used to register the episode.</param>
|
|
||||||
public RegisterSubtitle(IIdentifier identifier, ILibraryManager libraryManager)
|
|
||||||
{
|
|
||||||
_identifier = identifier;
|
|
||||||
_libraryManager = libraryManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
TaskParameter.CreateRequired<string>("path", "The path of the subtitle file")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
string path = arguments["path"].As<string>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
progress.Report(0);
|
|
||||||
Track track = await _identifier.IdentifyTrack(path);
|
|
||||||
progress.Report(25);
|
|
||||||
|
|
||||||
if (track.Episode == null)
|
|
||||||
throw new TaskFailedException($"No episode identified for the track at {path}");
|
|
||||||
if (track.Episode.ID == 0)
|
|
||||||
{
|
|
||||||
if (track.Episode.Slug != null)
|
|
||||||
track.Episode = await _libraryManager.Get<Episode>(track.Episode.Slug);
|
|
||||||
else if (track.Episode.Path != null)
|
|
||||||
{
|
|
||||||
track.Episode = await _libraryManager.GetOrDefault<Episode>(x => x.Path.StartsWith(track.Episode.Path));
|
|
||||||
if (track.Episode == null)
|
|
||||||
throw new TaskFailedException($"No episode found for the track at: {path}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new TaskFailedException($"No episode identified for the track at {path}");
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(50);
|
|
||||||
await _libraryManager.Create(track);
|
|
||||||
progress.Report(100);
|
|
||||||
}
|
|
||||||
catch (IdentificationFailedException ex)
|
|
||||||
{
|
|
||||||
throw new TaskFailedException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An endpoint to list and run tasks in the background.
|
|
||||||
/// </summary>
|
|
||||||
[Route("tasks")]
|
|
||||||
[Route("task", Order = AlternativeRoute)]
|
|
||||||
[ApiController]
|
|
||||||
[ResourceView]
|
|
||||||
[PartialPermission("Task", Group = Group.Admin)]
|
|
||||||
[ApiDefinition("Tasks", Group = AdminGroup)]
|
|
||||||
public class TaskApi : ControllerBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The task manager used to retrieve and start tasks.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ITaskManager _taskManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="TaskApi"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="taskManager">The task manager used to start tasks.</param>
|
|
||||||
public TaskApi(ITaskManager taskManager)
|
|
||||||
{
|
|
||||||
_taskManager = taskManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get all tasks
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Retrieve all tasks available in this instance of Kyoo.
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>A list of every tasks that this instance know.</returns>
|
|
||||||
[HttpGet]
|
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
public ActionResult<ICollection<ITask>> GetTasks()
|
|
||||||
{
|
|
||||||
return Ok(_taskManager.GetAllTasks());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start task
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Start a task with the given arguments. If a task is already running, it may be queued and started only when
|
|
||||||
/// a runner become available.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="taskSlug">The slug of the task to start.</param>
|
|
||||||
/// <param name="args">The list of arguments to give to the task.</param>
|
|
||||||
/// <returns>The task has been started or is queued.</returns>
|
|
||||||
/// <response code="400">The task misses an argument or an argument is invalid.</response>
|
|
||||||
/// <response code="404">No task could be found with the given slug.</response>
|
|
||||||
[HttpPut("{taskSlug}")]
|
|
||||||
[HttpGet("{taskSlug}", Order = AlternativeRoute)]
|
|
||||||
[PartialPermission(Kind.Create)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
||||||
public IActionResult RunTask(string taskSlug,
|
|
||||||
[FromQuery] Dictionary<string, object> args)
|
|
||||||
{
|
|
||||||
_taskManager.StartTask(taskSlug, new Progress<float>(), args);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,18 +40,13 @@ namespace Kyoo.Host
|
|||||||
/// Hosts of kyoo (main functions) generally only create a new <see cref="Application"/>
|
/// Hosts of kyoo (main functions) generally only create a new <see cref="Application"/>
|
||||||
/// and return <see cref="Start(string[])"/>.
|
/// and return <see cref="Start(string[])"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Application : IDisposable
|
public class Application
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The environment in witch Kyoo will run (ether "Production" or "Development").
|
/// The environment in witch Kyoo will run (ether "Production" or "Development").
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly string _environment;
|
private readonly string _environment;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The cancellation token source used to allow the app to be shutdown or restarted.
|
|
||||||
/// </summary>
|
|
||||||
private CancellationTokenSource _tokenSource;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger used for startup and error messages.
|
/// The logger used for startup and error messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -109,8 +104,7 @@ namespace Kyoo.Host
|
|||||||
.ConfigureContainer(configure)
|
.ConfigureContainer(configure)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_tokenSource = new CancellationTokenSource();
|
await _StartWithHost(host);
|
||||||
await _StartWithHost(host, _tokenSource.Token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -118,7 +112,7 @@ namespace Kyoo.Host
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="host">The host to start.</param>
|
/// <param name="host">The host to start.</param>
|
||||||
/// <param name="cancellationToken">A token to allow one to stop the host.</param>
|
/// <param name="cancellationToken">A token to allow one to stop the host.</param>
|
||||||
private async Task _StartWithHost(IHost host, CancellationToken cancellationToken)
|
private async Task _StartWithHost(IHost host, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -187,12 +181,5 @@ namespace Kyoo.Host
|
|||||||
.Enrich.WithThreadId()
|
.Enrich.WithThreadId()
|
||||||
.Enrich.FromLogContext();
|
.Enrich.FromLogContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_tokenSource.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.Loader;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -89,51 +86,11 @@ namespace Kyoo.Host.Controllers
|
|||||||
return _plugins;
|
return _plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load a single plugin and return all IPlugin implementations contained in the Assembly.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the dll</param>
|
|
||||||
/// <returns>The list of dlls in hte assembly</returns>
|
|
||||||
private IPlugin[] _LoadPlugin(string path)
|
|
||||||
{
|
|
||||||
path = Path.GetFullPath(path);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PluginDependencyLoader loader = new(path);
|
|
||||||
Assembly assembly = loader.LoadFromAssemblyPath(path);
|
|
||||||
return assembly.GetTypes()
|
|
||||||
.Where(x => typeof(IPlugin).IsAssignableFrom(x))
|
|
||||||
.Where(x => _plugins.All(y => y.GetType() != x))
|
|
||||||
.Select(x => (IPlugin)ActivatorUtilities.CreateInstance(_provider, x))
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Could not load the plugin at {Path}", path);
|
|
||||||
return Array.Empty<IPlugin>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void LoadPlugins(ICollection<IPlugin> plugins)
|
public void LoadPlugins(ICollection<IPlugin> plugins)
|
||||||
{
|
{
|
||||||
string pluginFolder = _options.Value.PluginPath;
|
_plugins.AddRange(plugins);
|
||||||
if (!Directory.Exists(pluginFolder))
|
_logger.LogInformation("Modules enabled: {Plugins}", _plugins.Select(x => x.Name));
|
||||||
Directory.CreateDirectory(pluginFolder);
|
|
||||||
|
|
||||||
_logger.LogTrace("Loading new plugins...");
|
|
||||||
string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories);
|
|
||||||
_plugins.AddRange(plugins
|
|
||||||
.Concat(pluginsPaths.SelectMany(_LoadPlugin))
|
|
||||||
.Where(x => x.Enabled)
|
|
||||||
.GroupBy(x => x.Name)
|
|
||||||
.Select(x => x.First())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!_plugins.Any())
|
|
||||||
_logger.LogInformation("No plugin enabled");
|
|
||||||
else
|
|
||||||
_logger.LogInformation("Plugin enabled: {Plugins}", _plugins.Select(x => x.Name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -144,52 +101,5 @@ namespace Kyoo.Host.Controllers
|
|||||||
.ToArray()
|
.ToArray()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A custom <see cref="AssemblyLoadContext"/> to load plugin's dependency if they are on the same folder.
|
|
||||||
/// </summary>
|
|
||||||
private class PluginDependencyLoader : AssemblyLoadContext
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The basic resolver that will be used to load dlls.
|
|
||||||
/// </summary>
|
|
||||||
private readonly AssemblyDependencyResolver _resolver;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="PluginDependencyLoader"/> for the given path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginPath">The path of the plugin and it's dependencies</param>
|
|
||||||
public PluginDependencyLoader(string pluginPath)
|
|
||||||
{
|
|
||||||
_resolver = new AssemblyDependencyResolver(pluginPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override Assembly Load(AssemblyName assemblyName)
|
|
||||||
{
|
|
||||||
Assembly existing = AppDomain.CurrentDomain.GetAssemblies()
|
|
||||||
.FirstOrDefault(x =>
|
|
||||||
{
|
|
||||||
AssemblyName name = x.GetName();
|
|
||||||
return name.Name == assemblyName.Name && name.Version == assemblyName.Version;
|
|
||||||
});
|
|
||||||
if (existing != null)
|
|
||||||
return existing;
|
|
||||||
// TODO load the assembly from the common folder if the file exists (this would allow shared libraries)
|
|
||||||
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
|
||||||
if (assemblyPath != null)
|
|
||||||
return LoadFromAssemblyPath(assemblyPath);
|
|
||||||
return base.Load(assemblyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
|
||||||
{
|
|
||||||
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
|
|
||||||
if (libraryPath != null)
|
|
||||||
return LoadUnmanagedDllFromPath(libraryPath);
|
|
||||||
return base.LoadUnmanagedDll(unmanagedDllName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,346 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Autofac.Features.Metadata;
|
|
||||||
using Autofac.Features.OwnedInstances;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
|
||||||
using Kyoo.Core.Models.Options;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace Kyoo.Host.Controllers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A service to handle long running tasks and a background runner.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Task will be queued, only one can run simultaneously.</remarks>
|
|
||||||
public class TaskManager : BackgroundService, ITaskManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The class representing task under this <see cref="TaskManager"/> jurisdiction.
|
|
||||||
/// </summary>
|
|
||||||
private class ManagedTask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The metadata for this task (the slug, and other useful information).
|
|
||||||
/// </summary>
|
|
||||||
public TaskMetadataAttribute Metadata { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The function used to create the task object.
|
|
||||||
/// </summary>
|
|
||||||
public Func<Owned<ITask>> Factory { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The next scheduled date for this task
|
|
||||||
/// </summary>
|
|
||||||
public DateTime ScheduledDate { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A class representing a task inside the <see cref="TaskManager._queuedTasks"/> list.
|
|
||||||
/// </summary>
|
|
||||||
private class QueuedTask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The task currently queued.
|
|
||||||
/// </summary>
|
|
||||||
public ManagedTask Task { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The progress reporter that this task should use.
|
|
||||||
/// </summary>
|
|
||||||
public IProgress<float> ProgressReporter { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The arguments to give to run the task with.
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, object> Arguments { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A token informing the task that it should be cancelled or not.
|
|
||||||
/// </summary>
|
|
||||||
public CancellationToken? CancellationToken { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The configuration instance used to get schedule information
|
|
||||||
/// </summary>
|
|
||||||
private readonly IOptionsMonitor<TaskOptions> _options;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The logger instance.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger<TaskManager> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of tasks and their next scheduled run.
|
|
||||||
/// </summary>
|
|
||||||
private readonly List<ManagedTask> _tasks;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The queue of tasks that should be run as soon as possible.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Queue<QueuedTask> _queuedTasks = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The cancellation token used to cancel the running task when the runner should shutdown.
|
|
||||||
/// </summary>
|
|
||||||
private readonly CancellationTokenSource _taskToken = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The currently running task.
|
|
||||||
/// </summary>
|
|
||||||
private (TaskMetadataAttribute, ITask)? _runningTask;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="TaskManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tasks">The list of tasks to manage with their metadata</param>
|
|
||||||
/// <param name="options">The configuration to load schedule information.</param>
|
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
public TaskManager(IEnumerable<Meta<Func<Owned<ITask>>, TaskMetadataAttribute>> tasks,
|
|
||||||
IOptionsMonitor<TaskOptions> options,
|
|
||||||
ILogger<TaskManager> logger)
|
|
||||||
{
|
|
||||||
_options = options;
|
|
||||||
_logger = logger;
|
|
||||||
_tasks = tasks.Select(x => new ManagedTask
|
|
||||||
{
|
|
||||||
Factory = x.Value,
|
|
||||||
Metadata = x.Metadata,
|
|
||||||
ScheduledDate = _GetNextTaskDate(x.Metadata.Slug)
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
if (_tasks.Any())
|
|
||||||
_logger.LogTrace("Task manager initiated with: {Tasks}", _tasks.Select(x => x.Metadata.Name));
|
|
||||||
else
|
|
||||||
_logger.LogInformation("Task manager initiated without any tasks");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Triggered when the application host is ready to start the service.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Start the runner in another thread.</remarks>
|
|
||||||
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
public override Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
Task.Run(() => base.StartAsync(cancellationToken), CancellationToken.None);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_taskToken.Cancel();
|
|
||||||
return base.StopAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The runner that will host tasks and run queued tasks.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">A token to stop the runner</param>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_EnqueueStartupTasks();
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (_queuedTasks.Any())
|
|
||||||
{
|
|
||||||
QueuedTask task = _queuedTasks.Dequeue();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _RunTask(task.Task, task.ProgressReporter, task.Arguments, task.CancellationToken);
|
|
||||||
}
|
|
||||||
catch (TaskFailedException ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("The task \"{Task}\" failed: {Message}",
|
|
||||||
task.Task.Metadata.Name, ex.Message);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.LogError(e, "An unhandled exception occured while running the task {Task}",
|
|
||||||
task.Task.Metadata.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Task.Delay(1000, cancellationToken);
|
|
||||||
_QueueScheduledTasks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parse parameters, inject a task and run it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="task">The task to run</param>
|
|
||||||
/// <param name="progress">A progress reporter to know the percentage of completion of the task.</param>
|
|
||||||
/// <param name="arguments">The arguments to pass to the function</param>
|
|
||||||
/// <param name="cancellationToken">An optional cancellation token that will be passed to the task.</param>
|
|
||||||
/// <exception cref="ArgumentException">
|
|
||||||
/// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument
|
|
||||||
/// invalid.
|
|
||||||
/// </exception>
|
|
||||||
private async Task _RunTask(ManagedTask task,
|
|
||||||
[NotNull] IProgress<float> progress,
|
|
||||||
Dictionary<string, object> arguments,
|
|
||||||
CancellationToken? cancellationToken = null)
|
|
||||||
{
|
|
||||||
using (_logger.BeginScope("Task: {Task}", task.Metadata.Name))
|
|
||||||
{
|
|
||||||
await using Owned<ITask> taskObj = task.Factory.Invoke();
|
|
||||||
ICollection<TaskParameter> all = taskObj.Value.GetParameters();
|
|
||||||
|
|
||||||
_runningTask = (task.Metadata, taskObj.Value);
|
|
||||||
ICollection<string> invalids = arguments.Keys
|
|
||||||
.Where(x => all.All(y => x != y.Name))
|
|
||||||
.ToArray();
|
|
||||||
if (invalids.Any())
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"{string.Join(", ", invalids)} are " +
|
|
||||||
$"invalid arguments for the task {task.Metadata.Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskParameters args = new(all
|
|
||||||
.Select(x =>
|
|
||||||
{
|
|
||||||
object value = arguments
|
|
||||||
.FirstOrDefault(y => string.Equals(y.Key, x.Name, StringComparison.OrdinalIgnoreCase))
|
|
||||||
.Value;
|
|
||||||
if (value == null && x.IsRequired)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"The argument {x.Name} is required to run " +
|
|
||||||
$"{task.Metadata.Name} but it was not specified.");
|
|
||||||
}
|
|
||||||
return x.CreateValue(value ?? x.DefaultValue);
|
|
||||||
}));
|
|
||||||
|
|
||||||
_logger.LogInformation("Task starting: {Task} ({Parameters})",
|
|
||||||
task.Metadata.Name, args.ToDictionary(x => x.Name, x => x.As<object>()));
|
|
||||||
|
|
||||||
CancellationToken token = cancellationToken != null
|
|
||||||
? CancellationTokenSource.CreateLinkedTokenSource(_taskToken.Token, cancellationToken.Value).Token
|
|
||||||
: _taskToken.Token;
|
|
||||||
await taskObj.Value.Run(args, progress, token);
|
|
||||||
|
|
||||||
_logger.LogInformation("Task finished: {Task}", task.Metadata.Name);
|
|
||||||
_runningTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start tasks that are scheduled for start.
|
|
||||||
/// </summary>
|
|
||||||
private void _QueueScheduledTasks()
|
|
||||||
{
|
|
||||||
IEnumerable<string> tasksToQueue = _tasks.Where(x => x.ScheduledDate <= DateTime.Now)
|
|
||||||
.Select(x => x.Metadata.Slug);
|
|
||||||
foreach (string task in tasksToQueue)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Queuing task scheduled for running: {Task}", task);
|
|
||||||
StartTask(task, new Progress<float>(), new Dictionary<string, object>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queue startup tasks with respect to the priority rules.
|
|
||||||
/// </summary>
|
|
||||||
private void _EnqueueStartupTasks()
|
|
||||||
{
|
|
||||||
IEnumerable<string> startupTasks = _tasks
|
|
||||||
.Where(x => x.Metadata.RunOnStartup)
|
|
||||||
.OrderByDescending(x => x.Metadata.Priority)
|
|
||||||
.Select(x => x.Metadata.Slug);
|
|
||||||
foreach (string task in startupTasks)
|
|
||||||
StartTask(task, new Progress<float>(), new Dictionary<string, object>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void StartTask(string taskSlug,
|
|
||||||
IProgress<float> progress,
|
|
||||||
Dictionary<string, object> arguments = null,
|
|
||||||
CancellationToken? cancellationToken = null)
|
|
||||||
{
|
|
||||||
arguments ??= new Dictionary<string, object>();
|
|
||||||
|
|
||||||
int index = _tasks.FindIndex(x => x.Metadata.Slug == taskSlug);
|
|
||||||
if (index == -1)
|
|
||||||
throw new ItemNotFoundException($"No task found with the slug {taskSlug}");
|
|
||||||
_queuedTasks.Enqueue(new QueuedTask
|
|
||||||
{
|
|
||||||
Task = _tasks[index],
|
|
||||||
ProgressReporter = progress,
|
|
||||||
Arguments = arguments,
|
|
||||||
CancellationToken = cancellationToken
|
|
||||||
});
|
|
||||||
_tasks[index].ScheduledDate = _GetNextTaskDate(taskSlug);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void StartTask<T>(IProgress<float> progress,
|
|
||||||
Dictionary<string, object> arguments = null,
|
|
||||||
CancellationToken? cancellationToken = null)
|
|
||||||
where T : ITask
|
|
||||||
{
|
|
||||||
TaskMetadataAttribute metadata = typeof(T).GetCustomAttribute<TaskMetadataAttribute>();
|
|
||||||
if (metadata == null)
|
|
||||||
throw new ArgumentException($"No metadata found on the given task (type: {typeof(T).Name}).");
|
|
||||||
StartTask(metadata.Slug, progress, arguments, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the next date of the execution of the given task.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="taskSlug">The slug of the task</param>
|
|
||||||
/// <returns>The next date.</returns>
|
|
||||||
private DateTime _GetNextTaskDate(string taskSlug)
|
|
||||||
{
|
|
||||||
if (_options.CurrentValue.Scheduled.TryGetValue(taskSlug, out TimeSpan delay))
|
|
||||||
return DateTime.Now + delay;
|
|
||||||
return DateTime.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks()
|
|
||||||
{
|
|
||||||
return _runningTask == null
|
|
||||||
? ArraySegment<(TaskMetadataAttribute, ITask)>.Empty
|
|
||||||
: new[] { _runningTask.Value };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ICollection<TaskMetadataAttribute> GetAllTasks()
|
|
||||||
{
|
|
||||||
return _tasks.Select(x => x.Metadata).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,13 +20,10 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Autofac.Extras.AttributeMetadata;
|
using Autofac.Extras.AttributeMetadata;
|
||||||
using Kyoo.Abstractions;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
using Kyoo.Core.Tasks;
|
|
||||||
using Kyoo.Host.Controllers;
|
using Kyoo.Host.Controllers;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Kyoo.Host
|
namespace Kyoo.Host
|
||||||
@ -36,15 +33,9 @@ namespace Kyoo.Host
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class HostModule : IPlugin
|
public class HostModule : IPlugin
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public string Slug => "host";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "Host";
|
public string Name => "Host";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Description => "A module that registers host controllers and other needed things.";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Dictionary<string, Type> Configuration => new()
|
public Dictionary<string, Type> Configuration => new()
|
||||||
{
|
{
|
||||||
@ -71,8 +62,6 @@ namespace Kyoo.Host
|
|||||||
builder.RegisterModule<AttributedMetadataModule>();
|
builder.RegisterModule<AttributedMetadataModule>();
|
||||||
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
|
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
|
||||||
builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope();
|
builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope();
|
||||||
builder.RegisterType<TaskManager>().As<ITaskManager>().As<IHostedService>().SingleInstance();
|
|
||||||
builder.RegisterTask<PluginInitializer>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
// Kyoo - A portable and vast media library solution.
|
|
||||||
// Copyright (c) Kyoo.
|
|
||||||
//
|
|
||||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
// Kyoo is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// Kyoo is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
|
|
||||||
namespace Kyoo.Core.Tasks
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A task run on Kyoo's startup to initialize plugins
|
|
||||||
/// </summary>
|
|
||||||
[TaskMetadata("plugin-init", "Plugin Initializer", "A task to initialize plugins.",
|
|
||||||
RunOnStartup = true, Priority = int.MaxValue, IsHidden = true)]
|
|
||||||
public class PluginInitializer : ITask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The plugin manager used to retrieve plugins to initialize them.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IPluginManager _pluginManager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The service provider given to each <see cref="IPlugin.Initialize"/> method.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IServiceProvider _provider;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="PluginInitializer"/> task
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginManager">The plugin manager used to retrieve plugins to initialize them.</param>
|
|
||||||
/// <param name="provider">The service provider given to each <see cref="IPlugin.Initialize"/> method.</param>
|
|
||||||
public PluginInitializer(IPluginManager pluginManager, IServiceProvider provider)
|
|
||||||
{
|
|
||||||
_pluginManager = pluginManager;
|
|
||||||
_provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public TaskParameters GetParameters()
|
|
||||||
{
|
|
||||||
return new();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
ICollection<IPlugin> plugins = _pluginManager.GetAllPlugins();
|
|
||||||
int count = 0;
|
|
||||||
progress.Report(0);
|
|
||||||
|
|
||||||
foreach (IPlugin plugin in plugins)
|
|
||||||
{
|
|
||||||
plugin.Initialize(_provider);
|
|
||||||
|
|
||||||
progress.Report(count / plugins.Count * 100);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.Report(100);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +1,10 @@
|
|||||||
{
|
{
|
||||||
"basics": {
|
"basics": {
|
||||||
"pluginsPath": "plugins/",
|
|
||||||
"transmuxPath": "cached/transmux",
|
"transmuxPath": "cached/transmux",
|
||||||
"transcodePath": "cached/transcode",
|
"transcodePath": "cached/transcode",
|
||||||
"metadataPath": "metadata/"
|
"metadataPath": "metadata/"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tasks": {
|
|
||||||
"parallels": "1",
|
|
||||||
"scheduled": {
|
|
||||||
"scan": "24:00:00"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"media": {
|
|
||||||
"regex": [
|
|
||||||
"^[\\/\\\\]*(?<Collection>.+)?[\\/\\\\]+(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?[\\/\\\\]+\\k<Show>(?: \\(\\d+\\))? S(?<Season>\\d+)E(?<Episode>\\d+)\\..*$",
|
|
||||||
"^[\\/\\\\]*(?<Collection>.+)?[\\/\\\\]+(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?[\\/\\\\]+\\k<Show>(?: \\(\\d+\\))? (?<Absolute>\\d+)\\..*$",
|
|
||||||
"^[\\/\\\\]*(?<Collection>.+)?[\\/\\\\]+(?<Show>.+?)(?: \\((?<StartYear>\\d+)\\))?[\\/\\\\]+\\k<Show>(?: \\(\\d+\\))?\\..*$"
|
|
||||||
],
|
|
||||||
"subtitleRegex": [
|
|
||||||
"^(?<Episode>.+)\\.(?<Language>\\w{1,3})\\.(?<Default>default\\.)?(?<Forced>forced\\.)?.*$"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"default": ["overall.read", "overall.write"],
|
"default": ["overall.read", "overall.write"],
|
||||||
|
@ -34,21 +34,12 @@ namespace Kyoo.Postgresql
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PostgresModule : IPlugin
|
public class PostgresModule : IPlugin
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public string Slug => "postgresql";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "Postgresql";
|
public string Name => "Postgresql";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Description => "A database context for postgresql.";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Dictionary<string, Type> Configuration => new();
|
public Dictionary<string, Type> Configuration => new();
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Enabled => true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The configuration to use. The database connection string is pulled from it.
|
/// The configuration to use. The database connection string is pulled from it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -18,14 +18,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NJsonSchema;
|
using NJsonSchema;
|
||||||
using NJsonSchema.Generation.TypeMappers;
|
using NJsonSchema.Generation.TypeMappers;
|
||||||
@ -40,32 +37,12 @@ namespace Kyoo.Swagger
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class SwaggerModule : IPlugin
|
public class SwaggerModule : IPlugin
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public string Slug => "swagger";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "Swagger";
|
public string Name => "Swagger";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Description => "A swagger interface and an OpenAPI endpoint to document Kyoo.";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Dictionary<string, Type> Configuration => new();
|
public Dictionary<string, Type> Configuration => new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The configuration instance used to retrieve the server's public url.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IConfiguration _configuration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="SwaggerModule"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">The configuration instance used to retrieve the server's public url.</param>
|
|
||||||
public SwaggerModule(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Configure(IServiceCollection services)
|
public void Configure(IServiceCollection services)
|
||||||
{
|
{
|
||||||
@ -133,7 +110,7 @@ namespace Kyoo.Swagger
|
|||||||
SA.New<IApplicationBuilder>(app => app.UseReDoc(x =>
|
SA.New<IApplicationBuilder>(app => app.UseReDoc(x =>
|
||||||
{
|
{
|
||||||
x.Path = "/doc";
|
x.Path = "/doc";
|
||||||
x.TransformToExternalPath = (internalUiRoute, request) => "/api" + internalUiRoute;
|
x.TransformToExternalPath = (internalUiRoute, _) => "/api" + internalUiRoute;
|
||||||
x.AdditionalSettings["theme"] = new
|
x.AdditionalSettings["theme"] = new
|
||||||
{
|
{
|
||||||
colors = new { primary = new { main = "#e13e13" } }
|
colors = new { primary = new { main = "#e13e13" } }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user