Reworking the plugin manager

This commit is contained in:
Zoe Roux 2021-04-29 23:59:46 +02:00
parent 833447ded8
commit 79995ea191
39 changed files with 394 additions and 253 deletions

View File

@ -18,7 +18,7 @@ namespace Kyoo.Controllers
/// Get the repository corresponding to the T item.
/// </summary>
/// <typeparam name="T">The type you want</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The repository corresponding</returns>
IRepository<T> GetRepository<T>() where T : class, IResource;
@ -82,7 +82,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="id">The id of the resource</param>
/// <typeparam name="T">The type of the resource</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The resource found</returns>
Task<T> Get<T>(int id) where T : class, IResource;
@ -91,7 +91,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="slug">The slug of the resource</param>
/// <typeparam name="T">The type of the resource</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The resource found</returns>
Task<T> Get<T>(string slug) where T : class, IResource;
@ -100,7 +100,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="where">The filter function.</param>
/// <typeparam name="T">The type of the resource</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The first resource found that match the where function</returns>
Task<T> Get<T>(Expression<Func<T, bool>> where) where T : class, IResource;
@ -109,7 +109,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(int showID, int seasonNumber);
@ -118,7 +118,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(string showSlug, int seasonNumber);
@ -128,7 +128,7 @@ namespace Kyoo.Controllers
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <param name="episodeNumber">The episode's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> Get(int showID, int seasonNumber, int episodeNumber);
@ -138,7 +138,7 @@ namespace Kyoo.Controllers
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <param name="episodeNumber">The episode's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
@ -147,7 +147,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="slug">The slug of the track</param>
/// <param name="type">The type (Video, Audio or Subtitle)</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The tracl found</returns>
Task<Track> Get(string slug, StreamType type = StreamType.Unknown);
@ -505,7 +505,7 @@ namespace Kyoo.Controllers
/// <param name="item">The resourcce to edit, it's ID can't change.</param>
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
/// <typeparam name="T">The type of resources</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The resource edited and completed by database's informations (related items & so on)</returns>
Task<T> Edit<T>(T item, bool resetOld) where T : class, IResource;
@ -514,7 +514,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="item">The resource to delete</param>
/// <typeparam name="T">The type of resource to delete</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete<T>(T item) where T : class, IResource;
/// <summary>
@ -522,7 +522,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="id">The id of the resource to delete</param>
/// <typeparam name="T">The type of resource to delete</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete<T>(int id) where T : class, IResource;
/// <summary>
@ -530,7 +530,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="slug">The slug of the resource to delete</param>
/// <typeparam name="T">The type of resource to delete</typeparam>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete<T>(string slug) where T : class, IResource;
}
}

View File

@ -1,6 +1,6 @@
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Unity;
namespace Kyoo.Controllers
@ -8,6 +8,8 @@ namespace Kyoo.Controllers
/// <summary>
/// A common interface used to discord plugins
/// </summary>
/// <remarks>You can inject services in the IPlugin constructor.
/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.</remarks>
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public interface IPlugin
{
@ -30,30 +32,33 @@ namespace Kyoo.Controllers
/// A list of services that are provided by this service. This allow other plugins to declare dependencies.
/// </summary>
/// <remarks>
/// The format should be the name of the interface ':' and the name of the implementation.
/// For a plugins that provide a new service named IService with a default implementation named Koala, that would
/// be "IService:Koala".
/// You should put directly the type that you will register in configure, Kyoo will detect by itself which
/// interfaces are implemented by your type.
/// </remarks>
string[] Provides { get; }
Type[] Provides { get; }
/// <summary>
/// A list of services that are required by this service.
/// The Core will warn the user that this plugin can't be loaded if a required service is not found.
/// </summary>
/// <remarks>
/// This is the same format as <see cref="Provides"/> but you may leave a blank implementation's name if you don't need a special one.
/// For example, if you need a service named IService but you don't care what implementation it will be, you can use
/// "IService:"
/// Put here the most complete type that are needed for your plugin to work. If you need a LibraryManager,
/// put typeof(ILibraryManager).
/// </remarks>
string[] Requires { get; }
Type[] Requires { get; }
/// <summary>
/// A configure method that will be run on plugin's startup.
/// </summary>
/// <param name="container">A unity container to register new services.</param>
/// <param name="config">The configuration, if you need values at config time (database connection strings...)</param>
void Configure(IUnityContainer container);
/// <summary>
/// An optional configuration step to allow a plugin to change asp net configurations.
/// WARNING: This is only called on Kyoo's startup so you must restart the app to apply this changes.
/// </summary>
/// <param name="app">The Asp.Net application builder. On most case it is not needed but you can use it to add asp net functionalities.</param>
/// <param name="debugMode">True if the app should run in debug mode.</param>
void Configure(IUnityContainer container, IConfiguration config, IApplicationBuilder app, bool debugMode);
void ConfigureAspNet(IApplicationBuilder app) {}
}
}

View File

@ -1,13 +1,39 @@
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
namespace Kyoo.Controllers
{
/// <summary>
/// A manager to load plugins and retrieve information from them.
/// </summary>
public interface IPluginManager
{
/// <summary>
/// Get a single plugin that match the type and name given.
/// </summary>
/// <param name="name">The name of the plugin</param>
/// <typeparam name="T">The type of the plugin</typeparam>
/// <exception cref="ItemNotFoundException">If no plugins match the query</exception>
/// <returns>A plugin that match the queries</returns>
public T GetPlugin<T>(string name);
public IEnumerable<T> GetPlugins<T>();
public IEnumerable<IPlugin> GetAllPlugins();
/// <summary>
/// Get all plugins of the given type.
/// </summary>
/// <typeparam name="T">The type of plugins to get</typeparam>
/// <returns>A list of plugins matching the given type or an empty list of none match.</returns>
public ICollection<T> GetPlugins<T>();
/// <summary>
/// Get all plugins currently running on Kyoo. This also includes deleted plugins if the app as not been restarted.
/// </summary>
/// <returns>All plugins currently loaded.</returns>
public ICollection<IPlugin> GetAllPlugins();
/// <summary>
/// Load new plugins from the plugin directory.
/// </summary>
/// <exception cref="MissingDependencyException">If a plugin can't be loaded because a dependency can't be resolved.</exception>
public void ReloadPlugins();
}
}

View File

@ -127,21 +127,21 @@ namespace Kyoo.Controllers
/// Get a resource from it's ID.
/// </summary>
/// <param name="id">The id of the resource</param>
/// <exception cref="ItemNotFound">If the item could not be found.</exception>
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
/// <returns>The resource found</returns>
Task<T> Get(int id);
/// <summary>
/// Get a resource from it's slug.
/// </summary>
/// <param name="slug">The slug of the resource</param>
/// <exception cref="ItemNotFound">If the item could not be found.</exception>
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
/// <returns>The resource found</returns>
Task<T> Get(string slug);
/// <summary>
/// Get the first resource that match the predicate.
/// </summary>
/// <param name="where">A predicate to filter the resource.</param>
/// <exception cref="ItemNotFound">If the item could not be found.</exception>
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
/// <returns>The resource found</returns>
Task<T> Get(Expression<Func<T, bool>> where);
@ -221,7 +221,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="edited">The resourcce to edit, it's ID can't change.</param>
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The resource edited and completed by database's informations (related items & so on)</returns>
Task<T> Edit([NotNull] T edited, bool resetOld);
@ -229,62 +229,62 @@ namespace Kyoo.Controllers
/// Delete a resource by it's ID
/// </summary>
/// <param name="id">The ID of the resource</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete(int id);
/// <summary>
/// Delete a resource by it's slug
/// </summary>
/// <param name="slug">The slug of the resource</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete(string slug);
/// <summary>
/// Delete a resource
/// </summary>
/// <param name="obj">The resource to delete</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete([NotNull] T obj);
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="objs">One or multiple resources to delete</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(params T[] objs) => DeleteRange(objs.AsEnumerable());
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="objs">An enumerable of resources to delete</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(IEnumerable<T> objs);
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="ids">One or multiple resources's id</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(params int[] ids) => DeleteRange(ids.AsEnumerable());
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="ids">An enumearble of resources's id</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(IEnumerable<int> ids);
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="slugs">One or multiple resources's slug</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable());
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="slugs">An enumerable of resources's slug</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange(IEnumerable<string> slugs);
/// <summary>
/// Delete a list of resources.
/// </summary>
/// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task DeleteRange([NotNull] Expression<Func<T, bool>> where);
}
@ -306,7 +306,7 @@ namespace Kyoo.Controllers
/// Get a show's slug from it's ID.
/// </summary>
/// <param name="showID">The ID of the show</param>
/// <exception cref="ItemNotFound">If a show with the given ID is not found.</exception>
/// <exception cref="ItemNotFoundException">If a show with the given ID is not found.</exception>
/// <returns>The show's slug</returns>
Task<string> GetSlug(int showID);
}
@ -321,7 +321,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(int showID, int seasonNumber);
@ -330,7 +330,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(string showSlug, int seasonNumber);
@ -362,7 +362,7 @@ namespace Kyoo.Controllers
/// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <param name="episodeNumber">The episode's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> Get(int showID, int seasonNumber, int episodeNumber);
/// <summary>
@ -371,7 +371,7 @@ namespace Kyoo.Controllers
/// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param>
/// <param name="episodeNumber">The episode's number</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
@ -397,7 +397,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="showID">The id of the show</param>
/// <param name="absoluteNumber">The episode's absolute number (The episode number does not reset to 1 after the end of a season.</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> GetAbsolute(int showID, int absoluteNumber);
/// <summary>
@ -405,7 +405,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="showSlug">The slug of the show</param>
/// <param name="absoluteNumber">The episode's absolute number (The episode number does not reset to 1 after the end of a season.</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The episode found</returns>
Task<Episode> GetAbsolute(string showSlug, int absoluteNumber);
}
@ -420,7 +420,7 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="slug">The slug of the track</param>
/// <param name="type">The type (Video, Audio or Subtitle)</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The tracl found</returns>
Task<Track> Get(string slug, StreamType type = StreamType.Unknown);

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
namespace Kyoo.Controllers
@ -17,7 +16,7 @@ namespace Kyoo.Controllers
/// <param name="taskSlug">The slug of the task to run</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>
/// <exception cref="ArgumentException">If the number of arguments is invalid or if an argument can't be converted.</exception>
/// <exception cref="ItemNotFound">The task could not be found.</exception>
/// <exception cref="ItemNotFoundException">The task could not be found.</exception>
void StartTask(string taskSlug, Dictionary<string, object> arguments = null);
/// <summary>
@ -27,7 +26,7 @@ namespace Kyoo.Controllers
ICollection<ITask> GetRunningTasks();
/// <summary>
/// Get all availables tasks
/// Get all available tasks
/// </summary>
/// <returns>A list of every tasks that this instance know.</returns>
ICollection<ITask> GetAllTasks();

View File

@ -1,5 +1,4 @@
using Kyoo.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;

View File

@ -66,7 +66,7 @@ namespace Kyoo.Controllers
{
if (_repositories.FirstOrDefault(x => x.RepositoryType == typeof(T)) is IRepository<T> ret)
return ret;
throw new ItemNotFound();
throw new ItemNotFoundException();
}
/// <inheritdoc />

View File

@ -2,18 +2,25 @@ using System;
namespace Kyoo.Models.Exceptions
{
/// <summary>
/// An exception raised when an item already exists in the database.
/// </summary>
[Serializable]
public class DuplicatedItemException : Exception
{
public override string Message { get; }
/// <summary>
/// Create a new <see cref="DuplicatedItemException"/> with the default message.
/// </summary>
public DuplicatedItemException()
{
Message = "Already exists in the databse.";
}
: base("Already exists in the database.")
{ }
/// <summary>
/// Create a new <see cref="DuplicatedItemException"/> with a custom message.
/// </summary>
/// <param name="message">The message to use</param>
public DuplicatedItemException(string message)
{
Message = message;
}
: base(message)
{ }
}
}

View File

@ -1,16 +0,0 @@
using System;
namespace Kyoo.Models.Exceptions
{
public class ItemNotFound : Exception
{
public override string Message { get; }
public ItemNotFound() {}
public ItemNotFound(string message)
{
Message = message;
}
}
}

View File

@ -0,0 +1,24 @@
using System;
namespace Kyoo.Models.Exceptions
{
/// <summary>
/// An exception raised when an item could not be found.
/// </summary>
[Serializable]
public class ItemNotFoundException : Exception
{
/// <summary>
/// Create a default <see cref="ItemNotFoundException"/> with no message.
/// </summary>
public ItemNotFoundException() {}
/// <summary>
/// Create a new <see cref="ItemNotFoundException"/> with a message
/// </summary>
/// <param name="message">The message of the exception</param>
public ItemNotFoundException(string message)
: base(message)
{ }
}
}

View File

@ -0,0 +1,20 @@
using System;
namespace Kyoo.Models.Exceptions
{
/// <summary>
/// An exception raised when a plugin requires dependencies that can't be found with the current configuration.
/// </summary>
[Serializable]
public class MissingDependencyException : Exception
{
/// <summary>
/// Create a new <see cref="MissingDependencyException"/> with a custom message
/// </summary>
/// <param name="plugin">The name of the plugin that can't be loaded.</param>
/// <param name="dependency">The name of the missing dependency.</param>
public MissingDependencyException(string plugin, string dependency)
: base($"No {dependency} are available in kyoo but the plugin {plugin} requires it.")
{}
}
}

View File

@ -33,7 +33,7 @@ namespace Kyoo.CommonApi
{
return await _repository.Get(id);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -47,7 +47,7 @@ namespace Kyoo.CommonApi
{
return await _repository.Get(slug);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -129,7 +129,7 @@ namespace Kyoo.CommonApi
resource.ID = old.ID;
return await _repository.Edit(resource, resetOld);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -144,7 +144,7 @@ namespace Kyoo.CommonApi
{
return await _repository.Edit(resource, resetOld);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -160,7 +160,7 @@ namespace Kyoo.CommonApi
resource.ID = old.ID;
return await _repository.Edit(resource, resetOld);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -174,7 +174,7 @@ namespace Kyoo.CommonApi
{
await _repository.Delete(id);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -190,7 +190,7 @@ namespace Kyoo.CommonApi
{
await _repository.Delete(slug);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -205,7 +205,7 @@ namespace Kyoo.CommonApi
{
await _repository.DeleteRange(ApiHelper.ParseWhere<T>(where));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -9,14 +9,15 @@ namespace Kyoo
public static class Extensions
{
/// <summary>
/// Get a connection string from the Configuration's section "Databse"
/// Get a connection string from the Configuration's section "Database"
/// </summary>
/// <param name="config">The IConfiguration instance to load.</param>
/// <param name="database">The database's name.</param>
/// <returns>A parsed connection string</returns>
public static string GetDatabaseConnection(this IConfiguration config)
public static string GetDatabaseConnection(this IConfiguration config, string database)
{
DbConnectionStringBuilder builder = new();
IConfigurationSection section = config.GetSection("Database");
IConfigurationSection section = config.GetSection("Database").GetSection(database);
foreach (IConfigurationSection child in section.GetChildren())
builder[child.Key] = child.Value;
return builder.ConnectionString;

View File

@ -46,13 +46,13 @@ namespace Kyoo.Controllers
/// Get a resource from it's ID and make the <see cref="Database"/> instance track it.
/// </summary>
/// <param name="id">The ID of the resource</param>
/// <exception cref="ItemNotFound">If the item is not found</exception>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The tracked resource with the given ID</returns>
protected virtual async Task<T> GetWithTracking(int id)
{
T ret = await Database.Set<T>().AsTracking().FirstOrDefaultAsync(x => x.ID == id);
if (ret == null)
throw new ItemNotFound($"No {typeof(T).Name} found with the id {id}");
throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}");
return ret;
}
@ -61,7 +61,7 @@ namespace Kyoo.Controllers
{
T ret = await GetOrDefault(id);
if (ret == null)
throw new ItemNotFound($"No {typeof(T).Name} found with the id {id}");
throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}");
return ret;
}
@ -70,7 +70,7 @@ namespace Kyoo.Controllers
{
T ret = await GetOrDefault(slug);
if (ret == null)
throw new ItemNotFound($"No {typeof(T).Name} found with the slug {slug}");
throw new ItemNotFoundException($"No {typeof(T).Name} found with the slug {slug}");
return ret;
}
@ -79,7 +79,7 @@ namespace Kyoo.Controllers
{
T ret = await GetOrDefault(where);
if (ret == null)
throw new ItemNotFound($"No {typeof(T).Name} found with the given predicate.");
throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
return ret;
}

View File

@ -2,10 +2,18 @@
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<OutputPath>$(SolutionDir)/Kyoo/bin/$(Configuration)/$(TargetFramework)/plugins/postgresql</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
<Company>SDG</Company>
<Authors>Zoe Roux</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo/Kyoo.csproj" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="5.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3">
@ -15,4 +23,15 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj">
<PrivateAssets>all</PrivateAssets>
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
<PrivateAssets>all</PrivateAssets>
<Private>false</Private>
</ProjectReference>
</ItemGroup>
</Project>

View File

@ -1,7 +1,6 @@
using System;
using Kyoo.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Npgsql;
namespace Kyoo.Postgresql

View File

@ -1,12 +1,9 @@
using System;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Unity;
using Unity.Injection;
using Unity.Lifetime;
using Unity.Resolution;
namespace Kyoo.Postgresql
{
@ -25,25 +22,42 @@ namespace Kyoo.Postgresql
public string Description => "A database context for postgresql.";
/// <inheritdoc />
public string[] Provides => new[]
public Type[] Provides => new[]
{
$"{nameof(DatabaseContext)}:{nameof(PostgresContext)}"
typeof(PostgresContext)
};
/// <inheritdoc />
public string[] Requires => Array.Empty<string>();
public Type[] Requires => Array.Empty<Type>();
/// <inheritdoc />
public void Configure(IUnityContainer container, IConfiguration config, IApplicationBuilder app, bool debugMode)
/// <summary>
/// The configuration to use. The database connection string is pulled from it.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// The host environment to check if the app is in debug mode.
/// </summary>
private readonly IWebHostEnvironment _environment;
/// <summary>
/// Create a new postgres module instance and use the given configuration and environment.
/// </summary>
/// <param name="configuration">The configuration to use</param>
/// <param name="env">The environment that will be used (if the env is in development mode, more information will be displayed on errors.</param>
public PostgresModule(IConfiguration configuration, IWebHostEnvironment env)
{
// options.UseNpgsql(_configuration.GetDatabaseConnection());
// // // .EnableSensitiveDataLogging()
// // // .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
container.RegisterFactory<DatabaseContext>(_ =>
{
return new PostgresContext(config.GetDatabaseConnection(), debugMode);
});
_configuration = configuration;
_environment = env;
}
/// <inheritdoc />
public void Configure(IUnityContainer container)
{
container.RegisterFactory<DatabaseContext>(_ => new PostgresContext(
_configuration.GetDatabaseConnection("postgres"),
_environment.IsDevelopment()));
}
}
}

View File

@ -4,100 +4,152 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity;
namespace Kyoo.Controllers
{
public class PluginDependencyLoader : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public PluginDependencyLoader(string pluginPath)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
return LoadFromAssemblyPath(assemblyPath);
return base.Load(assemblyName);
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libraryPath != null)
return LoadUnmanagedDllFromPath(libraryPath);
return base.LoadUnmanagedDll(unmanagedDllName);
}
}
/// <summary>
/// An implementation of <see cref="IPluginManager"/>.
/// This is used to load plugins and retrieve information from them.
/// </summary>
public class PluginManager : IPluginManager
{
private readonly IServiceProvider _provider;
/// <summary>
/// The unity container. It is given to the Configure method of plugins.
/// </summary>
private readonly IUnityContainer _container;
/// <summary>
/// The configuration to get the plugin's directory.
/// </summary>
private readonly IConfiguration _config;
private List<IPlugin> _plugins;
/// <summary>
/// The logger used by this class.
/// </summary>
private readonly ILogger<PluginManager> _logger;
/// <summary>
/// The list of plugins that are currently loaded.
/// </summary>
private readonly List<IPlugin> _plugins = new();
public PluginManager(IServiceProvider provider, IConfiguration config)
/// <summary>
/// Create a new <see cref="PluginManager"/> instance.
/// </summary>
/// <param name="container">A unity container to allow plugins to register new entries</param>
/// <param name="config">The configuration instance, to get the plugin's directory path.</param>
/// <param name="logger">The logger used by this class.</param>
public PluginManager(IUnityContainer container,
IConfiguration config,
ILogger<PluginManager> logger)
{
_provider = provider;
_container = container;
_config = config;
_logger = logger;
}
/// <inheritdoc />
public T GetPlugin<T>(string name)
{
return (T)_plugins?.FirstOrDefault(x => x.Name == name && x is T);
}
public IEnumerable<T> GetPlugins<T>()
/// <inheritdoc />
public ICollection<T> GetPlugins<T>()
{
return _plugins?.OfType<T>() ?? new List<T>();
return _plugins?.OfType<T>().ToArray();
}
public IEnumerable<IPlugin> GetAllPlugins()
/// <inheritdoc />
public ICollection<IPlugin> GetAllPlugins()
{
return _plugins ?? new List<IPlugin>();
return _plugins;
}
/// <inheritdoc />
public void ReloadPlugins()
{
string pluginFolder = _config.GetValue<string>("plugins");
if (!Directory.Exists(pluginFolder))
Directory.CreateDirectory(pluginFolder);
string[] pluginsPaths = Directory.GetFiles(pluginFolder);
_plugins = pluginsPaths.SelectMany(path =>
_logger.LogTrace("Loading new plugins...");
string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories);
ICollection<IPlugin> newPlugins = pluginsPaths.SelectMany(path =>
{
path = Path.GetFullPath(path);
try
{
PluginDependencyLoader loader = new(path);
Assembly ass = loader.LoadFromAssemblyPath(path);
return ass.GetTypes()
Assembly assembly = loader.LoadFromAssemblyPath(path);
return assembly.GetTypes()
.Where(x => typeof(IPlugin).IsAssignableFrom(x))
.Select(x => (IPlugin)ActivatorUtilities.CreateInstance(_provider, x));
.Where(x => _plugins.All(y => y.GetType() != x))
.Select(x => (IPlugin)_container.Resolve(x));
}
catch (Exception ex)
{
Console.Error.WriteLine($"\nError loading the plugin at {path}.\n{ex.GetType().Name}: {ex.Message}\n");
_logger.LogError(ex, "Could not load the plugin at {Path}", path);
return Array.Empty<IPlugin>();
}
}).ToList();
if (!_plugins.Any())
}).ToArray();
_plugins.AddRange(newPlugins);
ICollection<Type> available = _plugins.SelectMany(x => x.Provides).ToArray();
foreach (IPlugin plugin in newPlugins)
{
Console.WriteLine("\nNo plugin enabled.\n");
return;
Type missing = plugin.Requires.FirstOrDefault(x => available.All(y => !y.IsAssignableTo(x)));
if (missing != null)
throw new MissingDependencyException(plugin.Name, missing.Name);
plugin.Configure(_container);
}
Console.WriteLine("\nPlugin enabled:");
foreach (IPlugin plugin in _plugins)
Console.WriteLine($"\t{plugin.Name}");
Console.WriteLine();
if (!_plugins.Any())
_logger.LogInformation("No plugin enabled");
else
_logger.LogInformation("Plugin enabled: {Plugins}", _plugins.Select(x => x.Name));
}
/// <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)
{
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);
}
}
}
}

View File

@ -108,7 +108,7 @@ namespace Kyoo.Controllers
{
Episode ret = await GetOrDefault(showID, seasonNumber, episodeNumber);
if (ret == null)
throw new ItemNotFound($"No episode S{seasonNumber}E{episodeNumber} found on the show {showID}.");
throw new ItemNotFoundException($"No episode S{seasonNumber}E{episodeNumber} found on the show {showID}.");
return ret;
}
@ -117,7 +117,7 @@ namespace Kyoo.Controllers
{
Episode ret = await GetOrDefault(showSlug, seasonNumber, episodeNumber);
if (ret == null)
throw new ItemNotFound($"No episode S{seasonNumber}E{episodeNumber} found on the show {showSlug}.");
throw new ItemNotFoundException($"No episode S{seasonNumber}E{episodeNumber} found on the show {showSlug}.");
return ret;
}

View File

@ -154,7 +154,7 @@ namespace Kyoo.Controllers
sort,
limit);
if (!items.Any() && await _libraries.Value.GetOrDefault(id) == null)
throw new ItemNotFound();
throw new ItemNotFoundException();
return items;
}
@ -169,7 +169,7 @@ namespace Kyoo.Controllers
sort,
limit);
if (!items.Any() && await _libraries.Value.GetOrDefault(slug) == null)
throw new ItemNotFound();
throw new ItemNotFoundException();
return items;
}
}

View File

@ -131,7 +131,7 @@ namespace Kyoo.Controllers
sort,
limit);
if (!people.Any() && await _shows.Value.Get(showID) == null)
throw new ItemNotFound();
throw new ItemNotFoundException();
foreach (PeopleRole role in people)
role.ForPeople = true;
return people;
@ -153,7 +153,7 @@ namespace Kyoo.Controllers
sort,
limit);
if (!people.Any() && await _shows.Value.Get(showSlug) == null)
throw new ItemNotFound();
throw new ItemNotFoundException();
foreach (PeopleRole role in people)
role.ForPeople = true;
return people;
@ -174,7 +174,7 @@ namespace Kyoo.Controllers
sort,
limit);
if (!roles.Any() && await Get(id) == null)
throw new ItemNotFound();
throw new ItemNotFoundException();
return roles;
}
@ -193,7 +193,7 @@ namespace Kyoo.Controllers
sort,
limit);
if (!roles.Any() && await Get(slug) == null)
throw new ItemNotFound();
throw new ItemNotFoundException();
return roles;
}
}

View File

@ -88,7 +88,7 @@ namespace Kyoo.Controllers
{
Season ret = await GetOrDefault(showID, seasonNumber);
if (ret == null)
throw new ItemNotFound($"No season {seasonNumber} found for the show {showID}");
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showID}");
ret.ShowSlug = await _shows.GetSlug(showID);
return ret;
}
@ -98,7 +98,7 @@ namespace Kyoo.Controllers
{
Season ret = await GetOrDefault(showSlug, seasonNumber);
if (ret == null)
throw new ItemNotFound($"No season {seasonNumber} found for the show {showSlug}");
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showSlug}");
ret.ShowSlug = showSlug;
return ret;
}

View File

@ -46,7 +46,7 @@ namespace Kyoo.Controllers
{
Track ret = await GetOrDefault(slug, type);
if (ret == null)
throw new ItemNotFound($"No track found with the slug {slug} and the type {type}.");
throw new ItemNotFoundException($"No track found with the slug {slug} and the type {type}.");
return ret;
}

View File

@ -185,7 +185,7 @@ namespace Kyoo.Controllers
int index = _tasks.FindIndex(x => x.task.Slug == taskSlug);
if (index == -1)
throw new ItemNotFound($"No task found with the slug {taskSlug}");
throw new ItemNotFoundException($"No task found with the slug {taskSlug}");
_queuedTasks.Enqueue((_tasks[index].task, arguments));
_tasks[index] = (_tasks[index].task, DateTime.Now + GetTaskDelay(taskSlug));
}

View File

@ -1,7 +1,6 @@
using System;
using Kyoo.Controllers;
using Kyoo.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Unity;
using Unity.Lifetime;
@ -22,42 +21,40 @@ namespace Kyoo
public string Description => "The core module containing default implementations.";
/// <inheritdoc />
public string[] Provides => new[]
public Type[] Provides => new[]
{
$"{nameof(IFileManager)}:file",
$"{nameof(ITranscoder)}:{nameof(Transcoder)}",
$"{nameof(IThumbnailsManager)}:{nameof(ThumbnailsManager)}",
$"{nameof(IProviderManager)}:{nameof(ProviderManager)}",
$"{nameof(IPluginManager)}:{nameof(PluginManager)}",
$"{nameof(ITaskManager)}:{nameof(TaskManager)}",
$"{nameof(ILibraryManager)}:{nameof(LibraryManager)}",
$"{nameof(ILibraryRepository)}:{nameof(LibraryRepository)}",
$"{nameof(ILibraryItemRepository)}:{nameof(LibraryItemRepository)}",
$"{nameof(ICollectionRepository)}:{nameof(CollectionRepository)}",
$"{nameof(IShowRepository)}:{nameof(ShowRepository)}",
$"{nameof(ISeasonRepository)}:{nameof(SeasonRepository)}",
$"{nameof(IEpisodeRepository)}:{nameof(EpisodeRepository)}",
$"{nameof(ITrackRepository)}:{nameof(TrackRepository)}",
$"{nameof(IPeopleRepository)}:{nameof(PeopleRepository)}",
$"{nameof(IStudioRepository)}:{nameof(StudioRepository)}",
$"{nameof(IGenreRepository)}:{nameof(GenreRepository)}",
$"{nameof(IProviderRepository)}:{nameof(ProviderRepository)}"
typeof(FileManager),
typeof(Transcoder),
typeof(ThumbnailsManager),
typeof(ProviderManager),
typeof(TaskManager),
typeof(LibraryManager),
typeof(LibraryRepository),
typeof(LibraryItemRepository),
typeof(CollectionRepository),
typeof(ShowRepository),
typeof(SeasonRepository),
typeof(EpisodeRepository),
typeof(TrackRepository),
typeof(PeopleRepository),
typeof(StudioRepository),
typeof(GenreRepository),
typeof(ProviderRepository),
};
/// <inheritdoc />
public string[] Requires => new[]
public Type[] Requires => new[]
{
"DatabaseContext:"
typeof(DatabaseContext)
};
/// <inheritdoc />
public void Configure(IUnityContainer container, IConfiguration config, IApplicationBuilder app, bool debugMode)
public void Configure(IUnityContainer container)
{
container.RegisterType<IFileManager, FileManager>(new SingletonLifetimeManager());
container.RegisterType<ITranscoder, Transcoder>(new SingletonLifetimeManager());
container.RegisterType<IThumbnailsManager, ThumbnailsManager>(new SingletonLifetimeManager());
container.RegisterType<IProviderManager, ProviderManager>(new SingletonLifetimeManager());
container.RegisterType<IPluginManager, PluginManager>(new SingletonLifetimeManager());
container.RegisterType<ITaskManager, TaskManager>(new SingletonLifetimeManager());
container.RegisterType<ILibraryManager, LibraryManager>(new HierarchicalLifetimeManager());

View File

@ -5,9 +5,9 @@
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>../Kyoo.WebApp/</SpaRoot>
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
<TranscoderRoot>../Kyoo.Transcoder/</TranscoderRoot>
<SpaRoot>$(SolutionDir)/Kyoo.WebApp/</SpaRoot>
<LoginRoot>$(SolutionDir)/Kyoo.WebLogin/</LoginRoot>
<TranscoderRoot>$(SolutionDir)/Kyoo.Transcoder/</TranscoderRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules/**</DefaultItemExcludes>
<!-- Set this to true if you enable server-side prerendering -->

View File

@ -66,7 +66,7 @@ namespace Kyoo
}
/// <summary>
/// Createa a web host
/// Create a a web host
/// </summary>
/// <param name="args">Command line parameters that can be handled by kestrel</param>
/// <returns>A new web host instance</returns>

View File

@ -61,16 +61,9 @@ namespace Kyoo
});
services.AddHttpClient();
services.AddDbContext<DatabaseContext>(options =>
{
options.UseNpgsql(_configuration.GetDatabaseConnection());
// .EnableSensitiveDataLogging()
// .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
}, ServiceLifetime.Transient);
services.AddDbContext<IdentityDatabase>(options =>
{
options.UseNpgsql(_configuration.GetDatabaseConnection());
options.UseNpgsql(_configuration.GetDatabaseConnection("postgres"));
});
string assemblyName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
@ -94,13 +87,13 @@ namespace Kyoo
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(_configuration.GetDatabaseConnection(),
builder.UseNpgsql(_configuration.GetDatabaseConnection("postgres"),
sql => sql.MigrationsAssembly(assemblyName));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(_configuration.GetDatabaseConnection(),
builder.UseNpgsql(_configuration.GetDatabaseConnection("postgres"),
sql => sql.MigrationsAssembly(assemblyName));
options.EnableTokenCleanup = true;
})
@ -147,9 +140,6 @@ namespace Kyoo
{
AllowedOrigins = { new Uri(publicUrl).GetLeftPart(UriPartial.Authority) }
});
services.AddScoped<DbContext, DatabaseContext>();
}
public void Configure(IUnityContainer container, IApplicationBuilder app, IWebHostEnvironment env)
@ -214,11 +204,14 @@ namespace Kyoo
if (env.IsDevelopment())
spa.UseAngularCliServer("start");
});
container.RegisterType<IPluginManager, PluginManager>(new SingletonLifetimeManager());
IPluginManager pluginManager = new PluginManager(container, _configuration, new Logger<PluginManager>(_loggerFactory));
pluginManager.ReloadPlugins();
new CoreModule().Configure(container, _configuration, app, env.IsDevelopment());
container.RegisterFactory<IHostedService>(c => c.Resolve<ITaskManager>(), new SingletonLifetimeManager());
// TODO the reload should re inject components from the constructor.
// TODO fin a way to inject tasks without a IUnityContainer.
container.RegisterFactory<IHostedService>(c => c.Resolve<ITaskManager>(), new SingletonLifetimeManager());
}
}
}

View File

@ -177,7 +177,7 @@ namespace Kyoo.Tasks
await libraryManager.Create(track);
Console.WriteLine($"Registering subtitle at: {path}.");
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
await Console.Error.WriteLineAsync($"No episode found for subtitle at: ${path}.");
}
@ -317,7 +317,7 @@ namespace Kyoo.Tasks
season.Show = show;
return season;
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
Season season = await MetadataProvider.GetSeason(show, seasonNumber, library);
await libraryManager.CreateIfNotExists(season);

View File

@ -68,7 +68,7 @@ namespace Kyoo.Api
{
return await _libraryManager.Get(showSlug, seasonNumber);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -82,7 +82,7 @@ namespace Kyoo.Api
{
return await _libraryManager.Get(showID, seasonNumber);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -183,7 +183,7 @@ namespace Kyoo.Api
Episode episode = await _libraryManager.Get<Episode>(id);
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -198,7 +198,7 @@ namespace Kyoo.Api
Episode episode = await _libraryManager.Get<Episode>(slug);
return _files.FileResult(await _thumbnails.GetEpisodeThumb(episode));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -47,7 +47,7 @@ namespace Kyoo.Api
Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString(), StringComparer.InvariantCultureIgnoreCase),
limit);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -48,7 +48,7 @@ namespace Kyoo.Api
return Page(resources, limit);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -76,7 +76,7 @@ namespace Kyoo.Api
return Page(resources, limit);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -247,7 +247,7 @@ namespace Kyoo.Api
{
return await _libraryManager.Get<Studio>(x => x.Shows.Any(y => y.ID == showID));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -261,7 +261,7 @@ namespace Kyoo.Api
{
return await _libraryManager.Get<Studio>(x => x.Shows.Any(y => y.Slug == slug));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -384,7 +384,7 @@ namespace Kyoo.Api
.ToDictionary(Path.GetFileNameWithoutExtension,
x => $"{BaseURL}/api/shows/{slug}/fonts/{Path.GetFileName(x)}");
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -401,7 +401,7 @@ namespace Kyoo.Api
string path = Path.Combine(_files.GetExtraDirectory(show), "Attachments", slug);
return _files.FileResult(path);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -416,7 +416,7 @@ namespace Kyoo.Api
Show show = await _libraryManager.Get<Show>(slug);
return _files.FileResult(await _thumbs.GetShowPoster(show));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -431,7 +431,7 @@ namespace Kyoo.Api
Show show = await _libraryManager.Get<Show>(slug);
return _files.FileResult(await _thumbs.GetShowLogo(show));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -446,7 +446,7 @@ namespace Kyoo.Api
Show show = await _libraryManager.Get<Show>(slug);
return _files.FileResult(await _thumbs.GetShowBackdrop(show));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -36,7 +36,7 @@ namespace Kyoo.Api
_taskManager.StartTask(taskSlug, args);
return Ok();
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -31,7 +31,7 @@ namespace Kyoo.Api
{
return await _libraryManager.Get<Episode>(x => x.Tracks.Any(y => y.ID == id));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -47,7 +47,7 @@ namespace Kyoo.Api
// TODO Implement something like this (a dotnet-ef's QueryCompilationContext): https://stackoverflow.com/questions/62687811/how-can-i-convert-a-custom-function-to-a-sql-expression-for-entity-framework-cor
return await _libraryManager.Get<Episode>(x => x.Tracks.Any(y => y.Slug == slug));
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -52,7 +52,7 @@ namespace Kyoo.Api
Episode episode = await _libraryManager.Get<Episode>(slug);
return _files.FileResult(episode.Path, true);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -71,7 +71,7 @@ namespace Kyoo.Api
return StatusCode(500);
return _files.FileResult(path, true);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}
@ -90,7 +90,7 @@ namespace Kyoo.Api
return StatusCode(500);
return _files.FileResult(path, true);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -27,7 +27,7 @@ namespace Kyoo.Api
Episode item = await _libraryManager.Get<Episode>(slug);
return await WatchItem.FromEpisode(item, _libraryManager);
}
catch (ItemNotFound)
catch (ItemNotFoundException)
{
return NotFound();
}

View File

@ -3,14 +3,16 @@
"public_url": "http://localhost:5000/",
"database": {
"server": "127.0.0.1",
"port": "5432",
"database": "kyooDB",
"user ID": "kyoo",
"password": "kyooPassword",
"pooling": "true",
"maxPoolSize": "95",
"timeout": "30"
"postgres": {
"server": "127.0.0.1",
"port": "5432",
"database": "kyooDB",
"user ID": "kyoo",
"password": "kyooPassword",
"pooling": "true",
"maxPoolSize": "95",
"timeout": "30"
}
},
"logging": {