mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -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)]
|
||||
public interface IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// A slug to identify this plugin in queries.
|
||||
/// </summary>
|
||||
string Slug { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the plugin
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of this plugin. This will be displayed on the "installed plugins" page.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the plugin should be enabled, <c>false</c> otherwise.
|
||||
/// If a plugin is not enabled, no configure method will be called.
|
||||
/// This allow one to enable a plugin if a specific configuration value is set or if the environment contains
|
||||
/// the right settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default, a plugin is always enabled. This method can be overriden to change this behavior.
|
||||
/// </remarks>
|
||||
virtual bool Enabled => true;
|
||||
|
||||
/// <summary>
|
||||
/// A list of types that will be available via the IOptions interfaces and will be listed inside
|
||||
/// an IConfiguration.
|
||||
@ -73,7 +52,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// An optional configuration step to allow a plugin to change asp net configurations.
|
||||
/// </summary>
|
||||
/// <seealso cref="SA"/>
|
||||
virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
|
||||
IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// A configure method that will be run on plugin's startup.
|
||||
@ -94,15 +73,5 @@ namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
// 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
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using Autofac;
|
||||
using Autofac.Builder;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Utils;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Kyoo.Abstractions
|
||||
{
|
||||
@ -30,32 +28,6 @@ namespace Kyoo.Abstractions
|
||||
/// </summary>
|
||||
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>
|
||||
/// Register a new repository to the container.
|
||||
/// </summary>
|
||||
|
@ -35,15 +35,9 @@ namespace Kyoo.Authentication
|
||||
/// </summary>
|
||||
public class AuthenticationModule : IPlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Slug => "auth";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Authentication";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Enable an authentication/permission system for Kyoo (via Jwt or ApiKeys).";
|
||||
|
||||
/// <inheritdoc />
|
||||
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.Models.Utils;
|
||||
using Kyoo.Core.Controllers;
|
||||
using Kyoo.Core.Models.Options;
|
||||
using Kyoo.Core.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using IMetadataProvider = Kyoo.Abstractions.Controllers.IMetadataProvider;
|
||||
using JsonOptions = Kyoo.Core.Api.JsonOptions;
|
||||
|
||||
namespace Kyoo.Core
|
||||
@ -42,22 +39,13 @@ namespace Kyoo.Core
|
||||
/// </summary>
|
||||
public class CoreModule : IPlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Slug => "core";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Core";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "The core module containing default implementations.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, Type> Configuration => new()
|
||||
{
|
||||
{ TaskOptions.Path, typeof(TaskOptions) },
|
||||
{ MediaOptions.Path, typeof(MediaOptions) },
|
||||
{ "database", null },
|
||||
{ "logging", null }
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -69,17 +57,6 @@ namespace Kyoo.Core
|
||||
builder.RegisterType<Transcoder>().As<ITranscoder>().SingleInstance();
|
||||
builder.RegisterType<ThumbnailsManager>().As<IThumbnailsManager>().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<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>
|
||||
public const string Path = "Basics";
|
||||
|
||||
/// <summary>
|
||||
/// The path of the plugin directory.
|
||||
/// </summary>
|
||||
public string PluginPath { get; set; } = "plugins/";
|
||||
|
||||
/// <summary>
|
||||
/// The temporary folder to cache transmuxed file.
|
||||
/// </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"/>
|
||||
/// and return <see cref="Start(string[])"/>.
|
||||
/// </summary>
|
||||
public class Application : IDisposable
|
||||
public class Application
|
||||
{
|
||||
/// <summary>
|
||||
/// The environment in witch Kyoo will run (ether "Production" or "Development").
|
||||
/// </summary>
|
||||
private readonly string _environment;
|
||||
|
||||
/// <summary>
|
||||
/// The cancellation token source used to allow the app to be shutdown or restarted.
|
||||
/// </summary>
|
||||
private CancellationTokenSource _tokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// The logger used for startup and error messages.
|
||||
/// </summary>
|
||||
@ -109,8 +104,7 @@ namespace Kyoo.Host
|
||||
.ConfigureContainer(configure)
|
||||
.Build();
|
||||
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
await _StartWithHost(host, _tokenSource.Token);
|
||||
await _StartWithHost(host);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -118,7 +112,7 @@ namespace Kyoo.Host
|
||||
/// </summary>
|
||||
/// <param name="host">The host to start.</param>
|
||||
/// <param name="cancellationToken">A token to allow one to stop the host.</param>
|
||||
private async Task _StartWithHost(IHost host, CancellationToken cancellationToken)
|
||||
private async Task _StartWithHost(IHost host, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -187,12 +181,5 @@ namespace Kyoo.Host
|
||||
.Enrich.WithThreadId()
|
||||
.Enrich.FromLogContext();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
_tokenSource.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Core.Models.Options;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -89,51 +86,11 @@ namespace Kyoo.Host.Controllers
|
||||
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 />
|
||||
public void LoadPlugins(ICollection<IPlugin> plugins)
|
||||
{
|
||||
string pluginFolder = _options.Value.PluginPath;
|
||||
if (!Directory.Exists(pluginFolder))
|
||||
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));
|
||||
_plugins.AddRange(plugins);
|
||||
_logger.LogInformation("Modules enabled: {Plugins}", _plugins.Select(x => x.Name));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -144,52 +101,5 @@ namespace Kyoo.Host.Controllers
|
||||
.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 Autofac;
|
||||
using Autofac.Extras.AttributeMetadata;
|
||||
using Kyoo.Abstractions;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Core.Models.Options;
|
||||
using Kyoo.Core.Tasks;
|
||||
using Kyoo.Host.Controllers;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace Kyoo.Host
|
||||
@ -36,15 +33,9 @@ namespace Kyoo.Host
|
||||
/// </summary>
|
||||
public class HostModule : IPlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Slug => "host";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Host";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "A module that registers host controllers and other needed things.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, Type> Configuration => new()
|
||||
{
|
||||
@ -71,8 +62,6 @@ namespace Kyoo.Host
|
||||
builder.RegisterModule<AttributedMetadataModule>();
|
||||
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
|
||||
builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope();
|
||||
builder.RegisterType<TaskManager>().As<ITaskManager>().As<IHostedService>().SingleInstance();
|
||||
builder.RegisterTask<PluginInitializer>();
|
||||
}
|
||||
|
||||
/// <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": {
|
||||
"pluginsPath": "plugins/",
|
||||
"transmuxPath": "cached/transmux",
|
||||
"transcodePath": "cached/transcode",
|
||||
"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": {
|
||||
"permissions": {
|
||||
"default": ["overall.read", "overall.write"],
|
||||
|
@ -34,21 +34,12 @@ namespace Kyoo.Postgresql
|
||||
/// </summary>
|
||||
public class PostgresModule : IPlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Slug => "postgresql";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Postgresql";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "A database context for postgresql.";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, Type> Configuration => new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => true;
|
||||
|
||||
/// <summary>
|
||||
/// The configuration to use. The database connection string is pulled from it.
|
||||
/// </summary>
|
||||
|
@ -18,14 +18,11 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NJsonSchema;
|
||||
using NJsonSchema.Generation.TypeMappers;
|
||||
@ -40,32 +37,12 @@ namespace Kyoo.Swagger
|
||||
/// </summary>
|
||||
public class SwaggerModule : IPlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Slug => "swagger";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Swagger";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "A swagger interface and an OpenAPI endpoint to document Kyoo.";
|
||||
|
||||
/// <inheritdoc />
|
||||
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 />
|
||||
public void Configure(IServiceCollection services)
|
||||
{
|
||||
@ -133,7 +110,7 @@ namespace Kyoo.Swagger
|
||||
SA.New<IApplicationBuilder>(app => app.UseReDoc(x =>
|
||||
{
|
||||
x.Path = "/doc";
|
||||
x.TransformToExternalPath = (internalUiRoute, request) => "/api" + internalUiRoute;
|
||||
x.TransformToExternalPath = (internalUiRoute, _) => "/api" + internalUiRoute;
|
||||
x.AdditionalSettings["theme"] = new
|
||||
{
|
||||
colors = new { primary = new { main = "#e13e13" } }
|
||||
|
Loading…
x
Reference in New Issue
Block a user