Switch to file scopped namespaces

This commit is contained in:
Zoe Roux 2024-03-23 00:14:34 +01:00
parent 35e37bbe76
commit 18e301f26a
No known key found for this signature in database
99 changed files with 8936 additions and 9141 deletions

View File

@ -16,6 +16,8 @@ dotnet_diagnostic.IDE0055.severity = none
dotnet_diagnostic.IDE0058.severity = none dotnet_diagnostic.IDE0058.severity = none
dotnet_diagnostic.IDE0130.severity = none dotnet_diagnostic.IDE0130.severity = none
# Convert to file-scoped namespace
csharp_style_namespace_declarations = file_scoped:warning
# Sort using and Import directives with System.* appearing first # Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true
csharp_using_directive_placement = outside_namespace:warning csharp_using_directive_placement = outside_namespace:warning

View File

@ -18,13 +18,13 @@
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// An interface to interact with the database. Every repository is mapped through here.
/// </summary>
public interface ILibraryManager
{ {
/// <summary>
/// An interface to interact with the database. Every repository is mapped through here.
/// </summary>
public interface ILibraryManager
{
IRepository<T> Repository<T>() IRepository<T> Repository<T>()
where T : IResource, IQuery; where T : IResource, IQuery;
@ -77,5 +77,4 @@ namespace Kyoo.Abstractions.Controllers
/// The repository that handle users. /// The repository that handle users.
/// </summary> /// </summary>
IRepository<User> Users { get; } IRepository<User> Users { get; }
}
} }

View File

@ -19,13 +19,13 @@
using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Permissions;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A service to validate permissions.
/// </summary>
public interface IPermissionValidator
{ {
/// <summary>
/// A service to validate permissions.
/// </summary>
public interface IPermissionValidator
{
/// <summary> /// <summary>
/// Create an IAuthorizationFilter that will be used to validate permissions. /// Create an IAuthorizationFilter that will be used to validate permissions.
/// This can registered with any lifetime. /// This can registered with any lifetime.
@ -43,5 +43,4 @@ namespace Kyoo.Abstractions.Controllers
/// </param> /// </param>
/// <returns>An authorization filter used to validate the permission.</returns> /// <returns>An authorization filter used to validate the permission.</returns>
IFilterMetadata Create(PartialPermissionAttribute attribute); IFilterMetadata Create(PartialPermissionAttribute attribute);
}
} }

View File

@ -21,17 +21,17 @@ using System.Collections.Generic;
using Autofac; using Autofac;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A common interface used to discord plugins
/// </summary>
/// <remarks>
/// You can inject services in the IPlugin constructor.
/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.
/// </remarks>
public interface IPlugin
{ {
/// <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>
public interface IPlugin
{
/// <summary> /// <summary>
/// The name of the plugin /// The name of the plugin
/// </summary> /// </summary>
@ -62,5 +62,4 @@ namespace Kyoo.Abstractions.Controllers
{ {
// Skipped // Skipped
} }
}
} }

View File

@ -20,13 +20,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Abstractions.Models.Exceptions;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A manager to load plugins and retrieve information from them.
/// </summary>
public interface IPluginManager
{ {
/// <summary>
/// A manager to load plugins and retrieve information from them.
/// </summary>
public interface IPluginManager
{
/// <summary> /// <summary>
/// Get a single plugin that match the type and name given. /// Get a single plugin that match the type and name given.
/// </summary> /// </summary>
@ -66,5 +66,4 @@ namespace Kyoo.Abstractions.Controllers
/// You should not try to put plugins from the plugins directory here as they will get automatically loaded. /// You should not try to put plugins from the plugins directory here as they will get automatically loaded.
/// </param> /// </param>
public void LoadPlugins(params Type[] plugins); public void LoadPlugins(params Type[] plugins);
}
} }

View File

@ -23,15 +23,15 @@ using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Abstractions.Models.Utils; using Kyoo.Abstractions.Models.Utils;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
{
/// <summary> /// <summary>
/// A common repository for every resources. /// A common repository for every resources.
/// </summary> /// </summary>
/// <typeparam name="T">The resource's type that this repository manage.</typeparam> /// <typeparam name="T">The resource's type that this repository manage.</typeparam>
public interface IRepository<T> : IBaseRepository public interface IRepository<T> : IBaseRepository
where T : IResource, IQuery where T : IResource, IQuery
{ {
/// <summary> /// <summary>
/// The event handler type for all events of this repository. /// The event handler type for all events of this repository.
/// </summary> /// </summary>
@ -170,8 +170,7 @@ namespace Kyoo.Abstractions.Controllers
/// </summary> /// </summary>
/// <param name="obj">The resource newly created.</param> /// <param name="obj">The resource newly created.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected static Task OnResourceCreated(T obj) => protected static Task OnResourceCreated(T obj) => OnCreated?.Invoke(obj) ?? Task.CompletedTask;
OnCreated?.Invoke(obj) ?? Task.CompletedTask;
/// <summary> /// <summary>
/// Edit a resource and replace every property /// Edit a resource and replace every property
@ -203,8 +202,7 @@ namespace Kyoo.Abstractions.Controllers
/// </summary> /// </summary>
/// <param name="obj">The resource newly edited.</param> /// <param name="obj">The resource newly edited.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected static Task OnResourceEdited(T obj) => protected static Task OnResourceEdited(T obj) => OnEdited?.Invoke(obj) ?? Task.CompletedTask;
OnEdited?.Invoke(obj) ?? Task.CompletedTask;
/// <summary> /// <summary>
/// Delete a resource by it's ID /// Delete a resource by it's ID
@ -247,25 +245,23 @@ namespace Kyoo.Abstractions.Controllers
/// </summary> /// </summary>
/// <param name="obj">The resource newly deleted.</param> /// <param name="obj">The resource newly deleted.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected static Task OnResourceDeleted(T obj) => protected static Task OnResourceDeleted(T obj) => OnDeleted?.Invoke(obj) ?? Task.CompletedTask;
OnDeleted?.Invoke(obj) ?? Task.CompletedTask; }
}
/// <summary> /// <summary>
/// A base class for repositories. Every service implementing this will be handled by the <see cref="ILibraryManager"/>. /// A base class for repositories. Every service implementing this will be handled by the <see cref="ILibraryManager"/>.
/// </summary> /// </summary>
public interface IBaseRepository public interface IBaseRepository
{ {
/// <summary> /// <summary>
/// The type for witch this repository is responsible or null if non applicable. /// The type for witch this repository is responsible or null if non applicable.
/// </summary> /// </summary>
Type RepositoryType { get; } Type RepositoryType { get; }
} }
public interface IUserRepository : IRepository<User> public interface IUserRepository : IRepository<User>
{ {
Task<User?> GetByExternalId(string provider, string id); Task<User?> GetByExternalId(string provider, string id);
Task<User> AddExternalToken(Guid userId, string provider, ExternalToken token); Task<User> AddExternalToken(Guid userId, string provider, ExternalToken token);
Task<User> DeleteExternalToken(Guid userId, string provider); Task<User> DeleteExternalToken(Guid userId, string provider);
}
} }

View File

@ -21,13 +21,13 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// Download images and retrieve the path of those images for a resource.
/// </summary>
public interface IThumbnailsManager
{ {
/// <summary>
/// Download images and retrieve the path of those images for a resource.
/// </summary>
public interface IThumbnailsManager
{
/// <summary> /// <summary>
/// Download images of a specified item. /// Download images of a specified item.
/// If no images is available to download, do nothing and silently return. /// If no images is available to download, do nothing and silently return.
@ -75,5 +75,4 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="userId">The id of the user. </param> /// <param name="userId">The id of the user. </param>
/// <param name="image">The byte stream of the image. Null to delete the image.</param> /// <param name="image">The byte stream of the image. Null to delete the image.</param>
Task SetUserImage(Guid userId, Stream? image); Task SetUserImage(Guid userId, Stream? image);
}
} }

View File

@ -19,14 +19,14 @@
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// A list of constant priorities used for <see cref="IStartupAction"/>'s <see cref="IStartupAction.Priority"/>.
/// It also contains helper methods for creating new <see cref="StartupAction"/>.
/// </summary>
public static class SA
{ {
/// <summary>
/// A list of constant priorities used for <see cref="IStartupAction"/>'s <see cref="IStartupAction.Priority"/>.
/// It also contains helper methods for creating new <see cref="StartupAction"/>.
/// </summary>
public static class SA
{
/// <summary> /// <summary>
/// The highest predefined priority existing for <see cref="StartupAction"/>. /// The highest predefined priority existing for <see cref="StartupAction"/>.
/// </summary> /// </summary>
@ -102,10 +102,7 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T2">A second dependency that this action will use.</typeparam> /// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <typeparam name="T3">A third dependency that this action will use.</typeparam> /// <typeparam name="T3">A third dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns> /// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2, T3> New<T, T2, T3>( public static StartupAction<T, T2, T3> New<T, T2, T3>(Action<T, T2, T3> action, int priority)
Action<T, T2, T3> action,
int priority
)
where T : notnull where T : notnull
where T2 : notnull where T2 : notnull
where T3 : notnull => new(action, priority); where T3 : notnull => new(action, priority);
@ -249,16 +246,16 @@ namespace Kyoo.Abstractions.Controllers
); );
} }
} }
} }
/// <summary> /// <summary>
/// An action executed on kyoo's startup to initialize the asp-net container. /// An action executed on kyoo's startup to initialize the asp-net container.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is the base interface, see <see cref="SA.StartupAction"/> for a simpler use of this. /// This is the base interface, see <see cref="SA.StartupAction"/> for a simpler use of this.
/// </remarks> /// </remarks>
public interface IStartupAction public interface IStartupAction
{ {
/// <summary> /// <summary>
/// The priority of this action. The actions will be executed on descending priority order. /// The priority of this action. The actions will be executed on descending priority order.
/// If two actions have the same priority, their order is undefined. /// If two actions have the same priority, their order is undefined.
@ -270,5 +267,4 @@ namespace Kyoo.Abstractions.Controllers
/// </summary> /// </summary>
/// <param name="provider">The service provider containing all services can be used.</param> /// <param name="provider">The service provider containing all services can be used.</param>
void Run(IServiceProvider provider); void Run(IServiceProvider provider);
}
} }

View File

@ -23,13 +23,13 @@ using System.Security.Claims;
using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Abstractions.Models.Exceptions;
using Kyoo.Authentication.Models; using Kyoo.Authentication.Models;
namespace Kyoo.Authentication namespace Kyoo.Authentication;
/// <summary>
/// Extension methods.
/// </summary>
public static class Extensions
{ {
/// <summary>
/// Extension methods.
/// </summary>
public static class Extensions
{
/// <summary> /// <summary>
/// Get the permissions of an user. /// Get the permissions of an user.
/// </summary> /// </summary>
@ -61,5 +61,4 @@ namespace Kyoo.Authentication
throw new UnauthorizedException(); throw new UnauthorizedException();
return ret.Value; return ret.Value;
} }
}
} }

View File

@ -18,16 +18,16 @@
using System; using System;
namespace Kyoo.Abstractions.Models.Attributes namespace Kyoo.Abstractions.Models.Attributes;
/// <summary>
/// An attribute to specify on apis to specify it's documentation's name and category.
/// If this is applied on a method, the specified method will be exploded from the controller's page and be
/// included on the specified tag page.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ApiDefinitionAttribute : Attribute
{ {
/// <summary>
/// An attribute to specify on apis to specify it's documentation's name and category.
/// If this is applied on a method, the specified method will be exploded from the controller's page and be
/// included on the specified tag page.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ApiDefinitionAttribute : Attribute
{
/// <summary> /// <summary>
/// The public name of this api. /// The public name of this api.
/// </summary> /// </summary>
@ -48,5 +48,4 @@ namespace Kyoo.Abstractions.Models.Attributes
{ {
Name = name; Name = name;
} }
}
} }

View File

@ -18,11 +18,10 @@
using System; using System;
namespace Kyoo.Abstractions.Models.Attributes namespace Kyoo.Abstractions.Models.Attributes;
{
/// <summary> /// <summary>
/// An attribute to inform that the property is computed automatically and can't be assigned manually. /// An attribute to inform that the property is computed automatically and can't be assigned manually.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property)] [AttributeUsage(AttributeTargets.Property)]
public class ComputedAttribute : NotMergeableAttribute { } public class ComputedAttribute : NotMergeableAttribute { }
}

View File

@ -18,14 +18,14 @@
using System; using System;
namespace Kyoo.Abstractions.Models.Attributes namespace Kyoo.Abstractions.Models.Attributes;
/// <summary>
/// The targeted relation can be loaded.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class LoadableRelationAttribute : Attribute
{ {
/// <summary>
/// The targeted relation can be loaded.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class LoadableRelationAttribute : Attribute
{
/// <summary> /// <summary>
/// The name of the field containing the related resource's ID. /// The name of the field containing the related resource's ID.
/// </summary> /// </summary>
@ -50,5 +50,4 @@ namespace Kyoo.Abstractions.Models.Attributes
{ {
RelationID = relationID; RelationID = relationID;
} }
}
} }

View File

@ -18,23 +18,22 @@
using System; using System;
namespace Kyoo.Abstractions.Models.Attributes namespace Kyoo.Abstractions.Models.Attributes;
{
/// <summary>
/// Specify that a property can't be merged.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class NotMergeableAttribute : Attribute { }
/// <summary> /// <summary>
/// An interface with a method called when this object is merged. /// Specify that a property can't be merged.
/// </summary> /// </summary>
public interface IOnMerge [AttributeUsage(AttributeTargets.Property)]
{ public class NotMergeableAttribute : Attribute { }
/// <summary>
/// An interface with a method called when this object is merged.
/// </summary>
public interface IOnMerge
{
/// <summary> /// <summary>
/// This function is called after the object has been merged. /// This function is called after the object has been merged.
/// </summary> /// </summary>
/// <param name="merged">The object that has been merged with this.</param> /// <param name="merged">The object that has been merged with this.</param>
void OnMerge(object merged); void OnMerge(object merged);
}
} }

View File

@ -21,14 +21,14 @@ using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Models.Permissions namespace Kyoo.Abstractions.Models.Permissions;
/// <summary>
/// Specify one part of a permissions needed for the API (the kind or the type).
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PartialPermissionAttribute : Attribute, IFilterFactory
{ {
/// <summary>
/// Specify one part of a permissions needed for the API (the kind or the type).
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PartialPermissionAttribute : Attribute, IFilterFactory
{
/// <summary> /// <summary>
/// The needed permission type. /// The needed permission type.
/// </summary> /// </summary>
@ -84,5 +84,4 @@ namespace Kyoo.Abstractions.Models.Permissions
/// <inheritdoc /> /// <inheritdoc />
public bool IsReusable => true; public bool IsReusable => true;
}
} }

View File

@ -21,13 +21,13 @@ using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Abstractions.Models.Permissions namespace Kyoo.Abstractions.Models.Permissions;
/// <summary>
/// The kind of permission needed.
/// </summary>
public enum Kind
{ {
/// <summary>
/// The kind of permission needed.
/// </summary>
public enum Kind
{
/// <summary> /// <summary>
/// Allow the user to read for this kind of data. /// Allow the user to read for this kind of data.
/// </summary> /// </summary>
@ -52,13 +52,13 @@ namespace Kyoo.Abstractions.Models.Permissions
/// Allow the user to play this file. /// Allow the user to play this file.
/// </summary> /// </summary>
Play, Play,
} }
/// <summary> /// <summary>
/// The group of the permission. /// The group of the permission.
/// </summary> /// </summary>
public enum Group public enum Group
{ {
/// <summary> /// <summary>
/// Default group indicating no value. /// Default group indicating no value.
/// </summary> /// </summary>
@ -73,14 +73,14 @@ namespace Kyoo.Abstractions.Models.Permissions
/// Allow operation on sensitive items like libraries path, configurations and so on. /// Allow operation on sensitive items like libraries path, configurations and so on.
/// </summary> /// </summary>
Admin Admin
} }
/// <summary> /// <summary>
/// Specify permissions needed for the API. /// Specify permissions needed for the API.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class PermissionAttribute : Attribute, IFilterFactory public class PermissionAttribute : Attribute, IFilterFactory
{ {
/// <summary> /// <summary>
/// The needed permission as string. /// The needed permission as string.
/// </summary> /// </summary>
@ -133,5 +133,4 @@ namespace Kyoo.Abstractions.Models.Permissions
{ {
return Type; return Type;
} }
}
} }

View File

@ -18,14 +18,13 @@
using System; using System;
namespace Kyoo.Abstractions.Models.Permissions namespace Kyoo.Abstractions.Models.Permissions;
/// <summary>
/// The annotated route can only be accessed by a logged in user.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class UserOnlyAttribute : Attribute
{ {
/// <summary>
/// The annotated route can only be accessed by a logged in user.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class UserOnlyAttribute : Attribute
{
// TODO: Implement a Filter Attribute to make this work. For now, this attribute is only useful as documentation. // TODO: Implement a Filter Attribute to make this work. For now, this attribute is only useful as documentation.
}
} }

View File

@ -19,14 +19,14 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace Kyoo.Abstractions.Models.Exceptions namespace Kyoo.Abstractions.Models.Exceptions;
/// <summary>
/// An exception raised when an item already exists in the database.
/// </summary>
[Serializable]
public class DuplicatedItemException : Exception
{ {
/// <summary>
/// An exception raised when an item already exists in the database.
/// </summary>
[Serializable]
public class DuplicatedItemException : Exception
{
/// <summary> /// <summary>
/// The existing object. /// The existing object.
/// </summary> /// </summary>
@ -49,5 +49,4 @@ namespace Kyoo.Abstractions.Models.Exceptions
/// <param name="context">The serialization context</param> /// <param name="context">The serialization context</param>
protected DuplicatedItemException(SerializationInfo info, StreamingContext context) protected DuplicatedItemException(SerializationInfo info, StreamingContext context)
: base(info, context) { } : base(info, context) { }
}
} }

View File

@ -19,14 +19,14 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace Kyoo.Abstractions.Models.Exceptions namespace Kyoo.Abstractions.Models.Exceptions;
/// <summary>
/// An exception raised when an item could not be found.
/// </summary>
[Serializable]
public class ItemNotFoundException : Exception
{ {
/// <summary>
/// An exception raised when an item could not be found.
/// </summary>
[Serializable]
public class ItemNotFoundException : Exception
{
/// <summary> /// <summary>
/// Create a default <see cref="ItemNotFoundException"/> with no message. /// Create a default <see cref="ItemNotFoundException"/> with no message.
/// </summary> /// </summary>
@ -47,5 +47,4 @@ namespace Kyoo.Abstractions.Models.Exceptions
/// <param name="context">The serialization context</param> /// <param name="context">The serialization context</param>
protected ItemNotFoundException(SerializationInfo info, StreamingContext context) protected ItemNotFoundException(SerializationInfo info, StreamingContext context)
: base(info, context) { } : base(info, context) { }
}
} }

View File

@ -19,11 +19,11 @@
using System; using System;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace Kyoo.Abstractions.Models.Exceptions namespace Kyoo.Abstractions.Models.Exceptions;
[Serializable]
public class UnauthorizedException : Exception
{ {
[Serializable]
public class UnauthorizedException : Exception
{
public UnauthorizedException() public UnauthorizedException()
: base("User not authenticated or token invalid.") { } : base("User not authenticated or token invalid.") { }
@ -32,5 +32,4 @@ namespace Kyoo.Abstractions.Models.Exceptions
protected UnauthorizedException(SerializationInfo info, StreamingContext context) protected UnauthorizedException(SerializationInfo info, StreamingContext context)
: base(info, context) { } : base(info, context) { }
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// A genre that allow one to specify categories for shows.
/// </summary>
public enum Genre
{ {
/// <summary>
/// A genre that allow one to specify categories for shows.
/// </summary>
public enum Genre
{
Action, Action,
Adventure, Adventure,
Animation, Animation,
@ -41,5 +41,4 @@ namespace Kyoo.Abstractions.Models
Thriller, Thriller,
War, War,
Western, Western,
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// ID and link of an item on an external provider.
/// </summary>
public class MetadataId
{ {
/// <summary>
/// ID and link of an item on an external provider.
/// </summary>
public class MetadataId
{
/// <summary> /// <summary>
/// The ID of the resource on the external provider. /// The ID of the resource on the external provider.
/// </summary> /// </summary>
@ -32,5 +32,4 @@ namespace Kyoo.Abstractions.Models
/// The URL of the resource on the external provider. /// The URL of the resource on the external provider.
/// </summary> /// </summary>
public string? Link { get; set; } public string? Link { get; set; }
}
} }

View File

@ -20,15 +20,15 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
{
/// <summary> /// <summary>
/// A page of resource that contains information about the pagination of resources. /// A page of resource that contains information about the pagination of resources.
/// </summary> /// </summary>
/// <typeparam name="T">The type of resource contained in this page.</typeparam> /// <typeparam name="T">The type of resource contained in this page.</typeparam>
public class Page<T> public class Page<T>
where T : IResource where T : IResource
{ {
/// <summary> /// <summary>
/// The link of the current page. /// The link of the current page.
/// </summary> /// </summary>
@ -67,13 +67,7 @@ namespace Kyoo.Abstractions.Models
/// <param name="previous">The link of the previous page.</param> /// <param name="previous">The link of the previous page.</param>
/// <param name="next">The link of the next page.</param> /// <param name="next">The link of the next page.</param>
/// <param name="first">The link of the first page.</param> /// <param name="first">The link of the first page.</param>
public Page( public Page(ICollection<T> items, string @this, string? previous, string? next, string first)
ICollection<T> items,
string @this,
string? previous,
string? next,
string first
)
{ {
Items = items; Items = items;
This = @this; This = @this;
@ -108,5 +102,4 @@ namespace Kyoo.Abstractions.Models
query.Remove("afterID"); query.Remove("afterID");
First = url + query.ToQueryString(); First = url + query.ToQueryString();
} }
}
} }

View File

@ -23,13 +23,13 @@ using System.Text.Json.Serialization;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// A class representing collections of <see cref="Show"/>.
/// </summary>
public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, ILibraryItem
{ {
/// <summary>
/// A class representing collections of <see cref="Show"/>.
/// </summary>
public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, ILibraryItem
{
public static Sort DefaultSort => new Sort<Collection>.By(nameof(Collection.Name)); public static Sort DefaultSort => new Sort<Collection>.By(nameof(Collection.Name));
/// <inheritdoc /> /// <inheritdoc />
@ -87,5 +87,4 @@ namespace Kyoo.Abstractions.Models
Name = name; Name = name;
} }
} }
}
} }

View File

@ -26,13 +26,13 @@ using EntityFrameworkCore.Projectables;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// A class to represent a single show's episode.
/// </summary>
public class Episode : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, INews
{ {
/// <summary>
/// A class to represent a single show's episode.
/// </summary>
public class Episode : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, INews
{
// Use absolute numbers by default and fallback to season/episodes if it does not exists. // Use absolute numbers by default and fallback to season/episodes if it does not exists.
public static Sort DefaultSort => public static Sort DefaultSort =>
new Sort<Episode>.Conglomerate( new Sort<Episode>.Conglomerate(
@ -52,12 +52,7 @@ namespace Kyoo.Abstractions.Models
get get
{ {
if (ShowSlug != null || Show?.Slug != null) if (ShowSlug != null || Show?.Slug != null)
return GetSlug( return GetSlug(ShowSlug ?? Show!.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
ShowSlug ?? Show!.Slug,
SeasonNumber,
EpisodeNumber,
AbsoluteNumber
);
return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber); return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
} }
private set private set
@ -301,5 +296,4 @@ namespace Kyoo.Abstractions.Models
_ => $"{showSlug}-s{seasonNumber}e{episodeNumber}" _ => $"{showSlug}-s{seasonNumber}e{episodeNumber}"
}; };
} }
}
} }

View File

@ -18,16 +18,15 @@
using System; using System;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// An interface applied to resources.
/// </summary>
public interface IAddedDate
{ {
/// <summary>
/// An interface applied to resources.
/// </summary>
public interface IAddedDate
{
/// <summary> /// <summary>
/// The date at which this resource was added to kyoo. /// The date at which this resource was added to kyoo.
/// </summary> /// </summary>
public DateTime AddedDate { get; set; } public DateTime AddedDate { get; set; }
}
} }

View File

@ -18,16 +18,15 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// An interface applied to resources containing external metadata.
/// </summary>
public interface IMetadata
{ {
/// <summary>
/// An interface applied to resources containing external metadata.
/// </summary>
public interface IMetadata
{
/// <summary> /// <summary>
/// The link to metadata providers that this show has. See <see cref="MetadataId"/> for more information. /// The link to metadata providers that this show has. See <see cref="MetadataId"/> for more information.
/// </summary> /// </summary>
public Dictionary<string, MetadataId> ExternalId { get; set; } public Dictionary<string, MetadataId> ExternalId { get; set; }
}
} }

View File

@ -20,13 +20,13 @@ using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// An interface to represent a resource that can be retrieved from the database.
/// </summary>
public interface IResource : IQuery
{ {
/// <summary>
/// An interface to represent a resource that can be retrieved from the database.
/// </summary>
public interface IResource : IQuery
{
/// <summary> /// <summary>
/// A unique ID for this type of resource. This can't be changed and duplicates are not allowed. /// A unique ID for this type of resource. This can't be changed and duplicates are not allowed.
/// </summary> /// </summary>
@ -46,5 +46,4 @@ namespace Kyoo.Abstractions.Models
/// </remarks> /// </remarks>
[MaxLength(256)] [MaxLength(256)]
public string Slug { get; } public string Slug { get; }
}
} }

View File

@ -23,13 +23,13 @@ using System.Globalization;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// An interface representing items that contains images (like posters, thumbnails, logo, banners...)
/// </summary>
public interface IThumbnails
{ {
/// <summary>
/// An interface representing items that contains images (like posters, thumbnails, logo, banners...)
/// </summary>
public interface IThumbnails
{
/// <summary> /// <summary>
/// A poster is a 2/3 format image with the cover of the resource. /// A poster is a 2/3 format image with the cover of the resource.
/// </summary> /// </summary>
@ -45,12 +45,12 @@ namespace Kyoo.Abstractions.Models
/// A logo is a small image representing the resource. /// A logo is a small image representing the resource.
/// </summary> /// </summary>
public Image? Logo { get; set; } public Image? Logo { get; set; }
} }
[TypeConverter(typeof(ImageConvertor))] [TypeConverter(typeof(ImageConvertor))]
[SqlFirstColumn(nameof(Source))] [SqlFirstColumn(nameof(Source))]
public class Image public class Image
{ {
/// <summary> /// <summary>
/// The original image from another server. /// The original image from another server.
/// </summary> /// </summary>
@ -94,21 +94,18 @@ namespace Kyoo.Abstractions.Models
} }
/// <inheritdoc /> /// <inheritdoc />
public override bool CanConvertTo( public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
ITypeDescriptorContext? context,
Type? destinationType
)
{ {
return false; return false;
} }
} }
} }
/// <summary> /// <summary>
/// The quality of an image /// The quality of an image
/// </summary> /// </summary>
public enum ImageQuality public enum ImageQuality
{ {
/// <summary> /// <summary>
/// Small /// Small
/// </summary> /// </summary>
@ -123,5 +120,4 @@ namespace Kyoo.Abstractions.Models
/// Large /// Large
/// </summary> /// </summary>
High, High,
}
} }

View File

@ -27,12 +27,12 @@ using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
{
/// <summary> /// <summary>
/// A series or a movie. /// A series or a movie.
/// </summary> /// </summary>
public class Movie public class Movie
: IQuery, : IQuery,
IResource, IResource,
IMetadata, IMetadata,
@ -41,7 +41,7 @@ namespace Kyoo.Abstractions.Models
ILibraryItem, ILibraryItem,
INews, INews,
IWatchlist IWatchlist
{ {
public static Sort DefaultSort => new Sort<Movie>.By(x => x.Name); public static Sort DefaultSort => new Sort<Movie>.By(x => x.Name);
/// <inheritdoc /> /// <inheritdoc />
@ -185,5 +185,4 @@ namespace Kyoo.Abstractions.Models
Name = name; Name = name;
} }
} }
}
} }

View File

@ -26,13 +26,13 @@ using EntityFrameworkCore.Projectables;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// A season of a <see cref="Show"/>.
/// </summary>
public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
{ {
/// <summary>
/// A season of a <see cref="Show"/>.
/// </summary>
public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
{
public static Sort DefaultSort => new Sort<Season>.By(x => x.SeasonNumber); public static Sort DefaultSort => new Sort<Season>.By(x => x.SeasonNumber);
/// <inheritdoc /> /// <inheritdoc />
@ -145,5 +145,4 @@ namespace Kyoo.Abstractions.Models
public int EpisodesCount { get; set; } public int EpisodesCount { get; set; }
private int _EpisodesCount => Episodes!.Count; private int _EpisodesCount => Episodes!.Count;
}
} }

View File

@ -27,12 +27,12 @@ using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
{
/// <summary> /// <summary>
/// A series or a movie. /// A series or a movie.
/// </summary> /// </summary>
public class Show public class Show
: IQuery, : IQuery,
IResource, IResource,
IMetadata, IMetadata,
@ -41,7 +41,7 @@ namespace Kyoo.Abstractions.Models
IAddedDate, IAddedDate,
ILibraryItem, ILibraryItem,
IWatchlist IWatchlist
{ {
public static Sort DefaultSort => new Sort<Show>.By(x => x.Name); public static Sort DefaultSort => new Sort<Show>.By(x => x.Name);
/// <inheritdoc /> /// <inheritdoc />
@ -250,13 +250,13 @@ namespace Kyoo.Abstractions.Models
Name = name; Name = name;
} }
} }
} }
/// <summary> /// <summary>
/// The enum containing show's status. /// The enum containing show's status.
/// </summary> /// </summary>
public enum Status public enum Status
{ {
/// <summary> /// <summary>
/// The status of the show is not known. /// The status of the show is not known.
/// </summary> /// </summary>
@ -276,5 +276,4 @@ namespace Kyoo.Abstractions.Models
/// This show has not aired yet but has been announced. /// This show has not aired yet but has been announced.
/// </summary> /// </summary>
Planned Planned
}
} }

View File

@ -23,13 +23,13 @@ using System.Text.Json.Serialization;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// A studio that make shows.
/// </summary>
public class Studio : IQuery, IResource, IMetadata
{ {
/// <summary>
/// A studio that make shows.
/// </summary>
public class Studio : IQuery, IResource, IMetadata
{
public static Sort DefaultSort => new Sort<Studio>.By(x => x.Name); public static Sort DefaultSort => new Sort<Studio>.By(x => x.Name);
/// <inheritdoc /> /// <inheritdoc />
@ -77,5 +77,4 @@ namespace Kyoo.Abstractions.Models
Name = name; Name = name;
} }
} }
}
} }

View File

@ -23,13 +23,13 @@ using System.Text.Json.Serialization;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// A single user of the app.
/// </summary>
public class User : IQuery, IResource, IAddedDate
{ {
/// <summary>
/// A single user of the app.
/// </summary>
public class User : IQuery, IResource, IAddedDate
{
public static Sort DefaultSort => new Sort<User>.By(x => x.Username); public static Sort DefaultSort => new Sort<User>.By(x => x.Username);
/// <inheritdoc /> /// <inheritdoc />
@ -89,10 +89,10 @@ namespace Kyoo.Abstractions.Models
Username = username; Username = username;
} }
} }
} }
public class ExternalToken public class ExternalToken
{ {
/// <summary> /// <summary>
/// The id of this user on the external service. /// The id of this user on the external service.
/// </summary> /// </summary>
@ -113,5 +113,4 @@ namespace Kyoo.Abstractions.Models
/// Do not forget to refresh it when using it if necessary. /// Do not forget to refresh it when using it if necessary.
/// </summary> /// </summary>
public JwtToken Token { get; set; } public JwtToken Token { get; set; }
}
} }

View File

@ -20,13 +20,13 @@ using System;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// Has the user started watching, is it planned?
/// </summary>
public enum WatchStatus
{ {
/// <summary>
/// Has the user started watching, is it planned?
/// </summary>
public enum WatchStatus
{
/// <summary> /// <summary>
/// The user has already watched this. /// The user has already watched this.
/// </summary> /// </summary>
@ -46,14 +46,14 @@ namespace Kyoo.Abstractions.Models
/// The user has not started watching this but plans to. /// The user has not started watching this but plans to.
/// </summary> /// </summary>
Planned, Planned,
} }
/// <summary> /// <summary>
/// Metadata of what an user as started/planned to watch. /// Metadata of what an user as started/planned to watch.
/// </summary> /// </summary>
[SqlFirstColumn(nameof(UserId))] [SqlFirstColumn(nameof(UserId))]
public class MovieWatchStatus : IAddedDate public class MovieWatchStatus : IAddedDate
{ {
/// <summary> /// <summary>
/// The ID of the user that started watching this episode. /// The ID of the user that started watching this episode.
/// </summary> /// </summary>
@ -104,11 +104,11 @@ namespace Kyoo.Abstractions.Models
/// Null if the status is not Watching. /// Null if the status is not Watching.
/// </remarks> /// </remarks>
public int? WatchedPercent { get; set; } public int? WatchedPercent { get; set; }
} }
[SqlFirstColumn(nameof(UserId))] [SqlFirstColumn(nameof(UserId))]
public class EpisodeWatchStatus : IAddedDate public class EpisodeWatchStatus : IAddedDate
{ {
/// <summary> /// <summary>
/// The ID of the user that started watching this episode. /// The ID of the user that started watching this episode.
/// </summary> /// </summary>
@ -159,11 +159,11 @@ namespace Kyoo.Abstractions.Models
/// Null if the status is not Watching or if the next episode is not started. /// Null if the status is not Watching or if the next episode is not started.
/// </remarks> /// </remarks>
public int? WatchedPercent { get; set; } public int? WatchedPercent { get; set; }
} }
[SqlFirstColumn(nameof(UserId))] [SqlFirstColumn(nameof(UserId))]
public class ShowWatchStatus : IAddedDate public class ShowWatchStatus : IAddedDate
{ {
/// <summary> /// <summary>
/// The ID of the user that started watching this episode. /// The ID of the user that started watching this episode.
/// </summary> /// </summary>
@ -229,5 +229,4 @@ namespace Kyoo.Abstractions.Models
/// Null if the status is not Watching or if the next episode is not started. /// Null if the status is not Watching or if the next episode is not started.
/// </remarks> /// </remarks>
public int? WatchedPercent { get; set; } public int? WatchedPercent { get; set; }
}
} }

View File

@ -18,15 +18,15 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
{
/// <summary> /// <summary>
/// Results of a search request. /// Results of a search request.
/// </summary> /// </summary>
/// <typeparam name="T">The search item's type.</typeparam> /// <typeparam name="T">The search item's type.</typeparam>
public class SearchPage<T> : Page<T> public class SearchPage<T> : Page<T>
where T : IResource where T : IResource
{ {
public SearchPage( public SearchPage(
SearchResult result, SearchResult result,
string @this, string @this,
@ -50,5 +50,4 @@ namespace Kyoo.Abstractions.Models
public ICollection<T> Items { get; set; } public ICollection<T> Items { get; set; }
} }
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Authentication.Models namespace Kyoo.Authentication.Models;
/// <summary>
/// List of well known claims of kyoo
/// </summary>
public static class Claims
{ {
/// <summary>
/// List of well known claims of kyoo
/// </summary>
public static class Claims
{
/// <summary> /// <summary>
/// The id of the user /// The id of the user
/// </summary> /// </summary>
@ -52,5 +52,4 @@ namespace Kyoo.Authentication.Models
/// A guid used to identify a specific refresh token. This is only useful for the server to revokate tokens. /// A guid used to identify a specific refresh token. This is only useful for the server to revokate tokens.
/// </summary> /// </summary>
public static string Guid => "guid"; public static string Guid => "guid";
}
} }

View File

@ -18,13 +18,13 @@
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Abstractions.Models.Utils namespace Kyoo.Abstractions.Models.Utils;
/// <summary>
/// A class containing constant numbers.
/// </summary>
public static class Constants
{ {
/// <summary>
/// A class containing constant numbers.
/// </summary>
public static class Constants
{
/// <summary> /// <summary>
/// A property to use on a Microsoft.AspNet.MVC.Route.Order property to mark it as an alternative route /// A property to use on a Microsoft.AspNet.MVC.Route.Order property to mark it as an alternative route
/// that won't be included on the swagger. /// that won't be included on the swagger.
@ -56,5 +56,4 @@ namespace Kyoo.Abstractions.Models.Utils
/// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for endpoints used by admins. /// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for endpoints used by admins.
/// </summary> /// </summary>
public const string AdminGroup = "4:Admin"; public const string AdminGroup = "4:Admin";
}
} }

View File

@ -24,16 +24,16 @@ using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
namespace Kyoo.Abstractions.Models.Utils namespace Kyoo.Abstractions.Models.Utils;
/// <summary>
/// A class that represent a resource. It is made to be used as a parameter in a query and not used somewhere else
/// on the application.
/// This class allow routes to be used via ether IDs or Slugs, this is suitable for every <see cref="IResource"/>.
/// </summary>
[TypeConverter(typeof(IdentifierConvertor))]
public class Identifier
{ {
/// <summary>
/// A class that represent a resource. It is made to be used as a parameter in a query and not used somewhere else
/// on the application.
/// This class allow routes to be used via ether IDs or Slugs, this is suitable for every <see cref="IResource"/>.
/// </summary>
[TypeConverter(typeof(IdentifierConvertor))]
public class Identifier
{
/// <summary> /// <summary>
/// The ID of the resource or null if the slug is specified. /// The ID of the resource or null if the slug is specified.
/// </summary> /// </summary>
@ -162,9 +162,7 @@ namespace Kyoo.Abstractions.Models.Utils
public Filter<T> IsSame<T>() public Filter<T> IsSame<T>()
where T : IResource where T : IResource
{ {
return _id.HasValue return _id.HasValue ? new Filter<T>.Eq("Id", _id.Value) : new Filter<T>.Eq("Slug", _slug!);
? new Filter<T>.Eq("Id", _id.Value)
: new Filter<T>.Eq("Slug", _slug!);
} }
public bool Is(Guid uid) public bool Is(Guid uid)
@ -244,5 +242,4 @@ namespace Kyoo.Abstractions.Models.Utils
return Guid.TryParse(slug, out id) ? new Identifier(id) : new Identifier(slug); return Guid.TryParse(slug, out id) ? new Identifier(id) : new Identifier(slug);
} }
} }
}
} }

View File

@ -18,13 +18,13 @@
using System; using System;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// Information about the pagination. How many items should be displayed and where to start.
/// </summary>
public class Pagination
{ {
/// <summary>
/// Information about the pagination. How many items should be displayed and where to start.
/// </summary>
public class Pagination
{
/// <summary> /// <summary>
/// The count of items to return. /// The count of items to return.
/// </summary> /// </summary>
@ -69,5 +69,4 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">Set the <see cref="Limit"/> value</param> /// <param name="limit">Set the <see cref="Limit"/> value</param>
/// <returns>A new <see cref="Pagination"/> instance</returns> /// <returns>A new <see cref="Pagination"/> instance</returns>
public static implicit operator Pagination(int limit) => new(limit); public static implicit operator Pagination(int limit) => new(limit);
}
} }

View File

@ -19,13 +19,13 @@
using System; using System;
using System.Linq; using System.Linq;
namespace Kyoo.Abstractions.Models.Utils namespace Kyoo.Abstractions.Models.Utils;
/// <summary>
/// The list of errors that where made in the request.
/// </summary>
public class RequestError
{ {
/// <summary>
/// The list of errors that where made in the request.
/// </summary>
public class RequestError
{
/// <summary> /// <summary>
/// The list of errors that where made in the request. /// The list of errors that where made in the request.
/// </summary> /// </summary>
@ -50,11 +50,7 @@ namespace Kyoo.Abstractions.Models.Utils
public RequestError(string[] errors) public RequestError(string[] errors)
{ {
if (errors == null || !errors.Any()) if (errors == null || !errors.Any())
throw new ArgumentException( throw new ArgumentException("Errors must be non null and not empty", nameof(errors));
"Errors must be non null and not empty",
nameof(errors)
);
Errors = errors; Errors = errors;
} }
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
/// <summary>
/// Information about the pagination. How many items should be displayed and where to start.
/// </summary>
public class SearchPagination
{ {
/// <summary>
/// Information about the pagination. How many items should be displayed and where to start.
/// </summary>
public class SearchPagination
{
/// <summary> /// <summary>
/// The count of items to return. /// The count of items to return.
/// </summary> /// </summary>
@ -32,5 +32,4 @@ namespace Kyoo.Abstractions.Controllers
/// Where to start? How many items to skip? /// Where to start? How many items to skip?
/// </summary> /// </summary>
public int? Skip { get; set; } public int? Skip { get; set; }
}
} }

View File

@ -25,17 +25,17 @@ using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers;
{
public record Sort;
/// <summary> public record Sort;
/// Information about how a query should be sorted. What factor should decide the sort and in which order.
/// </summary> /// <summary>
/// <typeparam name="T">For witch type this sort applies</typeparam> /// Information about how a query should be sorted. What factor should decide the sort and in which order.
public record Sort<T> : Sort /// </summary>
/// <typeparam name="T">For witch type this sort applies</typeparam>
public record Sort<T> : Sort
where T : IQuery where T : IQuery
{ {
/// <summary> /// <summary>
/// Sort by a specific key /// Sort by a specific key
/// </summary> /// </summary>
@ -117,8 +117,7 @@ namespace Kyoo.Abstractions.Controllers
) )
}; };
Type[] types = Type[] types = typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
PropertyInfo? property = types PropertyInfo? property = types
.Select(x => .Select(x =>
x.GetProperty( x.GetProperty(
@ -131,5 +130,4 @@ namespace Kyoo.Abstractions.Controllers
throw new ValidationException("The given sort key is not valid."); throw new ValidationException("The given sort key is not valid.");
return new By(property.Name, desendant); return new By(property.Name, desendant);
} }
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Abstractions.Models namespace Kyoo.Abstractions.Models;
/// <summary>
/// The links to see a movie or an episode.
/// </summary>
public class VideoLinks
{ {
/// <summary>
/// The links to see a movie or an episode.
/// </summary>
public class VideoLinks
{
/// <summary> /// <summary>
/// The direct link to the unprocessed video (pristine quality). /// The direct link to the unprocessed video (pristine quality).
/// </summary> /// </summary>
@ -32,5 +32,4 @@ namespace Kyoo.Abstractions.Models
/// The link to an HLS master playlist containing all qualities available for this video. /// The link to an HLS master playlist containing all qualities available for this video.
/// </summary> /// </summary>
public string Hls { get; set; } public string Hls { get; set; }
}
} }

View File

@ -21,13 +21,13 @@ using Autofac.Builder;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Utils; using Kyoo.Utils;
namespace Kyoo.Abstractions namespace Kyoo.Abstractions;
/// <summary>
/// A static class with helper functions to setup external modules
/// </summary>
public static class Module
{ {
/// <summary>
/// A static class with helper functions to setup external modules
/// </summary>
public static class Module
{
/// <summary> /// <summary>
/// Register a new repository to the container. /// Register a new repository to the container.
/// </summary> /// </summary>
@ -72,5 +72,4 @@ namespace Kyoo.Abstractions
{ {
return builder.RegisterRepository<T2>().AsSelf().As<T>(); return builder.RegisterRepository<T2>().AsSelf().As<T>();
} }
}
} }

View File

@ -19,13 +19,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Kyoo.Utils namespace Kyoo.Utils;
/// <summary>
/// A set of extensions class for enumerable.
/// </summary>
public static class EnumerableExtensions
{ {
/// <summary>
/// A set of extensions class for enumerable.
/// </summary>
public static class EnumerableExtensions
{
/// <summary> /// <summary>
/// If the enumerable is empty, execute an action. /// If the enumerable is empty, execute an action.
/// </summary> /// </summary>
@ -67,5 +67,4 @@ namespace Kyoo.Utils
foreach (T i in self) foreach (T i in self)
action(i); action(i);
} }
}
} }

View File

@ -22,13 +22,13 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Utils namespace Kyoo.Utils;
/// <summary>
/// A class containing helper methods to merge objects.
/// </summary>
public static class Merger
{ {
/// <summary>
/// A class containing helper methods to merge objects.
/// </summary>
public static class Merger
{
/// <summary> /// <summary>
/// Merge two dictionary, if the same key is found on both dictionary, the values of the second one is kept. /// Merge two dictionary, if the same key is found on both dictionary, the values of the second one is kept.
/// </summary> /// </summary>
@ -130,5 +130,4 @@ namespace Kyoo.Utils
merge.OnMerge(second); merge.OnMerge(second);
return first; return first;
} }
}
} }

View File

@ -25,13 +25,13 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Kyoo.Utils namespace Kyoo.Utils;
/// <summary>
/// A set of utility functions that can be used everywhere.
/// </summary>
public static class Utility
{ {
/// <summary>
/// A set of utility functions that can be used everywhere.
/// </summary>
public static class Utility
{
/// <summary> /// <summary>
/// Convert a string to snake case. Stollen from /// Convert a string to snake case. Stollen from
/// https://github.com/efcore/EFCore.NamingConventions/blob/main/EFCore.NamingConventions/Internal/SnakeCaseNameRewriter.cs /// https://github.com/efcore/EFCore.NamingConventions/blob/main/EFCore.NamingConventions/Internal/SnakeCaseNameRewriter.cs
@ -204,9 +204,7 @@ namespace Kyoo.Utils
: type.GetInheritanceTree(); : type.GetInheritanceTree();
return types return types
.Prepend(type) .Prepend(type)
.FirstOrDefault(x => .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
x.IsGenericType && x.GetGenericTypeDefinition() == genericType
);
} }
/// <summary> /// <summary>
@ -361,5 +359,4 @@ namespace Kyoo.Utils
return string.Empty; return string.Empty;
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}")); return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
} }
}
} }

View File

@ -32,19 +32,19 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace Kyoo.Authentication namespace Kyoo.Authentication;
{
/// <summary> /// <summary>
/// A module that enable OpenID authentication for Kyoo. /// A module that enable OpenID authentication for Kyoo.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Create a new authentication module instance and use the given configuration. /// Create a new authentication module instance and use the given configuration.
/// </remarks> /// </remarks>
public class AuthenticationModule( public class AuthenticationModule(
IConfiguration configuration, IConfiguration configuration,
ILogger<AuthenticationModule> logger ILogger<AuthenticationModule> logger
) : IPlugin ) : IPlugin
{ {
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Authentication"; public string Name => "Authentication";
@ -78,10 +78,7 @@ namespace Kyoo.Authentication
NewUser = _configuration NewUser = _configuration
.GetValue("DEFAULT_PERMISSIONS", "overall.read,overall.play")! .GetValue("DEFAULT_PERMISSIONS", "overall.read,overall.play")!
.Split(','), .Split(','),
RequireVerification = _configuration.GetValue( RequireVerification = _configuration.GetValue("REQUIRE_ACCOUNT_VERIFICATION", true),
"REQUIRE_ACCOUNT_VERIFICATION",
true
),
PublicUrl = PublicUrl =
_configuration.GetValue<string?>("PUBLIC_URL") ?? "http://localhost:8901", _configuration.GetValue<string?>("PUBLIC_URL") ?? "http://localhost:8901",
ApiKeys = _configuration.GetValue("KYOO_APIKEYS", string.Empty)!.Split(','), ApiKeys = _configuration.GetValue("KYOO_APIKEYS", string.Empty)!.Split(','),
@ -154,10 +151,7 @@ namespace Kyoo.Authentication
{ {
string prefix = "Bearer "; string prefix = "Bearer ";
if ( if (
ctx.Request.Headers.TryGetValue( ctx.Request.Headers.TryGetValue("Authorization", out StringValues val)
"Authorization",
out StringValues val
)
&& val.ToString() is string auth && val.ToString() is string auth
&& auth.StartsWith(prefix) && auth.StartsWith(prefix)
) )
@ -185,5 +179,4 @@ namespace Kyoo.Authentication
{ {
SA.New<IApplicationBuilder>(app => app.UseAuthentication(), SA.Authentication), SA.New<IApplicationBuilder>(app => app.UseAuthentication(), SA.Authentication),
}; };
}
} }

View File

@ -21,13 +21,13 @@ using System.Threading.Tasks;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace Kyoo.Authentication namespace Kyoo.Authentication;
/// <summary>
/// The service that controls jwt creation and validation.
/// </summary>
public interface ITokenController
{ {
/// <summary>
/// The service that controls jwt creation and validation.
/// </summary>
public interface ITokenController
{
/// <summary> /// <summary>
/// Create a new access token for the given user. /// Create a new access token for the given user.
/// </summary> /// </summary>
@ -50,5 +50,4 @@ namespace Kyoo.Authentication
/// <exception cref="SecurityTokenException">The given refresh token is not valid.</exception> /// <exception cref="SecurityTokenException">The given refresh token is not valid.</exception>
/// <returns>The id of the token's user.</returns> /// <returns>The id of the token's user.</returns>
Guid GetRefreshTokenUserID(string refreshToken); Guid GetRefreshTokenUserID(string refreshToken);
}
} }

View File

@ -32,14 +32,14 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
namespace Kyoo.Authentication namespace Kyoo.Authentication;
/// <summary>
/// A permission validator to validate permission with user Permission array
/// or the default array from the configurations if the user is not logged.
/// </summary>
public class PermissionValidator : IPermissionValidator
{ {
/// <summary>
/// A permission validator to validate permission with user Permission array
/// or the default array from the configurations if the user is not logged.
/// </summary>
public class PermissionValidator : IPermissionValidator
{
/// <summary> /// <summary>
/// The permissions options to retrieve default permissions. /// The permissions options to retrieve default permissions.
/// </summary> /// </summary>
@ -126,11 +126,7 @@ namespace Kyoo.Authentication
/// <param name="partialInfo">The partial permission to validate.</param> /// <param name="partialInfo">The partial permission to validate.</param>
/// <param name="group">The group of the permission.</param> /// <param name="group">The group of the permission.</param>
/// <param name="options">The option containing default values.</param> /// <param name="options">The option containing default values.</param>
public PermissionValidatorFilter( public PermissionValidatorFilter(object partialInfo, Group? group, PermissionOption options)
object partialInfo,
Group? group,
PermissionOption options
)
{ {
switch (partialInfo) switch (partialInfo)
{ {
@ -159,11 +155,7 @@ namespace Kyoo.Authentication
if (permission == null || kind == null) if (permission == null || kind == null)
{ {
if ( if (context.HttpContext.Items["PermissionGroup"] is Group group and not Group.None)
context.HttpContext.Items["PermissionGroup"]
is Group group
and not Group.None
)
_group = group; _group = group;
else if (_group == Group.None) else if (_group == Group.None)
_group = Group.Overall; _group = Group.Overall;
@ -226,10 +218,7 @@ namespace Kyoo.Authentication
} }
} }
else if (res.Failure != null) else if (res.Failure != null)
context.Result = _ErrorResult( context.Result = _ErrorResult(res.Failure.Message, StatusCodes.Status403Forbidden);
res.Failure.Message,
StatusCodes.Status403Forbidden
);
else else
context.Result = _ErrorResult( context.Result = _ErrorResult(
"Authentication panic", "Authentication panic",
@ -277,9 +266,7 @@ namespace Kyoo.Authentication
); );
// Change the failure message to make the API nice to use. // Change the failure message to make the API nice to use.
if (ret.Failure != null) if (ret.Failure != null)
return AuthenticateResult.Fail( return AuthenticateResult.Fail("Invalid JWT token. The token may have expired.");
"Invalid JWT token. The token may have expired."
);
return ret; return ret;
} }
} }
@ -294,5 +281,4 @@ namespace Kyoo.Authentication
{ {
return new ObjectResult(new RequestError(error)) { StatusCode = code }; return new ObjectResult(new RequestError(error)) { StatusCode = code };
} }
}
} }

View File

@ -27,13 +27,13 @@ using Kyoo.Abstractions.Models;
using Kyoo.Authentication.Models; using Kyoo.Authentication.Models;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace Kyoo.Authentication namespace Kyoo.Authentication;
/// <summary>
/// The service that controls jwt creation and validation.
/// </summary>
public class TokenController : ITokenController
{ {
/// <summary>
/// The service that controls jwt creation and validation.
/// </summary>
public class TokenController : ITokenController
{
/// <summary> /// <summary>
/// The options that this controller will use. /// The options that this controller will use.
/// </summary> /// </summary>
@ -131,5 +131,4 @@ namespace Kyoo.Authentication
return id; return id;
throw new SecurityTokenException("Token not associated to any user."); throw new SecurityTokenException("Token not associated to any user.");
} }
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Authentication.Models.DTO namespace Kyoo.Authentication.Models.DTO;
/// <summary>
/// A model only used on login requests.
/// </summary>
public class LoginRequest
{ {
/// <summary>
/// A model only used on login requests.
/// </summary>
public class LoginRequest
{
/// <summary> /// <summary>
/// The user's username. /// The user's username.
/// </summary> /// </summary>
@ -43,5 +43,4 @@ namespace Kyoo.Authentication.Models.DTO
Username = username; Username = username;
Password = password; Password = password;
} }
}
} }

View File

@ -21,13 +21,13 @@ using Kyoo.Abstractions.Models;
using Kyoo.Utils; using Kyoo.Utils;
using BCryptNet = BCrypt.Net.BCrypt; using BCryptNet = BCrypt.Net.BCrypt;
namespace Kyoo.Authentication.Models.DTO namespace Kyoo.Authentication.Models.DTO;
/// <summary>
/// A model only used on register requests.
/// </summary>
public class RegisterRequest
{ {
/// <summary>
/// A model only used on register requests.
/// </summary>
public class RegisterRequest
{
/// <summary> /// <summary>
/// The user email address /// The user email address
/// </summary> /// </summary>
@ -73,5 +73,4 @@ namespace Kyoo.Authentication.Models.DTO
Email = Email, Email = Email,
}; };
} }
}
} }

View File

@ -16,13 +16,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>. // along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
namespace Kyoo.Authentication.Models namespace Kyoo.Authentication.Models;
/// <summary>
/// The main authentication options.
/// </summary>
public class AuthenticationOption
{ {
/// <summary>
/// The main authentication options.
/// </summary>
public class AuthenticationOption
{
/// <summary> /// <summary>
/// The path to get this option from the root configuration. /// The path to get this option from the root configuration.
/// </summary> /// </summary>
@ -42,5 +42,4 @@ namespace Kyoo.Authentication.Models
/// Options for permissions /// Options for permissions
/// </summary> /// </summary>
public PermissionOption Permissions { get; set; } = new(); public PermissionOption Permissions { get; set; } = new();
}
} }

View File

@ -35,22 +35,22 @@ using Microsoft.IdentityModel.Tokens;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
using BCryptNet = BCrypt.Net.BCrypt; using BCryptNet = BCrypt.Net.BCrypt;
namespace Kyoo.Authentication.Views namespace Kyoo.Authentication.Views;
{
/// <summary> /// <summary>
/// Sign in, Sign up or refresh tokens. /// Sign in, Sign up or refresh tokens.
/// </summary> /// </summary>
[ApiController] [ApiController]
[Route("auth")] [Route("auth")]
[ApiDefinition("Authentication", Group = UsersGroup)] [ApiDefinition("Authentication", Group = UsersGroup)]
public class AuthApi( public class AuthApi(
IUserRepository users, IUserRepository users,
OidcController oidc, OidcController oidc,
ITokenController tokenController, ITokenController tokenController,
IThumbnailsManager thumbs, IThumbnailsManager thumbs,
PermissionOption options PermissionOption options
) : ControllerBase ) : ControllerBase
{ {
/// <summary> /// <summary>
/// Create a new Forbidden result from an object. /// Create a new Forbidden result from an object.
/// </summary> /// </summary>
@ -127,12 +127,7 @@ namespace Kyoo.Authentication.Views
/// <response code="403">The provider gave an error.</response> /// <response code="403">The provider gave an error.</response>
[HttpGet("logged/{provider}")] [HttpGet("logged/{provider}")]
[ProducesResponseType(StatusCodes.Status302Found)] [ProducesResponseType(StatusCodes.Status302Found)]
public ActionResult OauthCodeRedirect( public ActionResult OauthCodeRedirect(string provider, string code, string state, string? error)
string provider,
string code,
string state,
string? error
)
{ {
return Redirect( return Redirect(
_BuildUrl( _BuildUrl(
@ -494,5 +489,4 @@ namespace Kyoo.Authentication.Views
await thumbs.SetUserImage(User.GetIdOrThrow(), null); await thumbs.SetUserImage(User.GetIdOrThrow(), null);
return NoContent(); return NoContent();
} }
}
} }

View File

@ -20,13 +20,13 @@ using Kyoo.Abstractions.Models.Utils;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// The route constraint that goes with the <see cref="Identifier"/>.
/// </summary>
public class IdentifierRouteConstraint : IRouteConstraint
{ {
/// <summary>
/// The route constraint that goes with the <see cref="Identifier"/>.
/// </summary>
public class IdentifierRouteConstraint : IRouteConstraint
{
/// <inheritdoc /> /// <inheritdoc />
public bool Match( public bool Match(
HttpContext? httpContext, HttpContext? httpContext,
@ -38,5 +38,4 @@ namespace Kyoo.Core.Controllers
{ {
return values.ContainsKey(routeKey); return values.ContainsKey(routeKey);
} }
}
} }

View File

@ -20,13 +20,13 @@ using System.Linq;
using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// An class to interact with the database. Every repository is mapped through here.
/// </summary>
public class LibraryManager : ILibraryManager
{ {
/// <summary>
/// An class to interact with the database. Every repository is mapped through here.
/// </summary>
public class LibraryManager : ILibraryManager
{
private readonly IBaseRepository[] _repositories; private readonly IBaseRepository[] _repositories;
public LibraryManager( public LibraryManager(
@ -102,5 +102,4 @@ namespace Kyoo.Core.Controllers
{ {
return (IRepository<T>)_repositories.First(x => x.RepositoryType == typeof(T)); return (IRepository<T>)_repositories.First(x => x.RepositoryType == typeof(T));
} }
}
} }

View File

@ -26,13 +26,13 @@ using Kyoo.Abstractions.Models.Utils;
using Kyoo.Postgresql; using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle collections
/// </summary>
public class CollectionRepository : LocalRepository<Collection>
{ {
/// <summary>
/// A local repository to handle collections
/// </summary>
public class CollectionRepository : LocalRepository<Collection>
{
/// <summary> /// <summary>
/// The database handle /// The database handle
/// </summary> /// </summary>
@ -99,5 +99,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
await base.Delete(obj); await base.Delete(obj);
} }
}
} }

View File

@ -27,13 +27,13 @@ using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle episodes.
/// </summary>
public class EpisodeRepository : LocalRepository<Episode>
{ {
/// <summary>
/// A local repository to handle episodes.
/// </summary>
public class EpisodeRepository : LocalRepository<Episode>
{
/// <summary> /// <summary>
/// The database handle /// The database handle
/// </summary> /// </summary>
@ -47,8 +47,7 @@ namespace Kyoo.Core.Controllers
IRepository<Show>.OnEdited += async (show) => IRepository<Show>.OnEdited += async (show) =>
{ {
await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope();
DatabaseContext database = DatabaseContext database = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
scope.ServiceProvider.GetRequiredService<DatabaseContext>();
List<Episode> episodes = await database List<Episode> episodes = await database
.Episodes.AsTracking() .Episodes.AsTracking()
.Where(x => x.ShowId == show.Id) .Where(x => x.ShowId == show.Id)
@ -152,5 +151,4 @@ namespace Kyoo.Core.Controllers
if (epCount == 1) if (epCount == 1)
await _shows.Delete(obj.ShowId); await _shows.Delete(obj.ShowId);
} }
}
} }

View File

@ -25,13 +25,13 @@ using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
using Kyoo.Abstractions.Models.Utils; using Kyoo.Abstractions.Models.Utils;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle library items.
/// </summary>
public class LibraryItemRepository : DapperRepository<ILibraryItem>
{ {
/// <summary>
/// A local repository to handle library items.
/// </summary>
public class LibraryItemRepository : DapperRepository<ILibraryItem>
{
// language=PostgreSQL // language=PostgreSQL
protected override FormattableString Sql => protected override FormattableString Sql =>
$""" $"""
@ -123,5 +123,4 @@ namespace Kyoo.Core.Controllers
limit ?? new() limit ?? new()
); );
} }
}
} }

View File

@ -32,15 +32,15 @@ using Kyoo.Postgresql;
using Kyoo.Utils; using Kyoo.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
{
/// <summary> /// <summary>
/// A base class to create repositories using Entity Framework. /// A base class to create repositories using Entity Framework.
/// </summary> /// </summary>
/// <typeparam name="T">The type of this repository</typeparam> /// <typeparam name="T">The type of this repository</typeparam>
public abstract class LocalRepository<T> : IRepository<T> public abstract class LocalRepository<T> : IRepository<T>
where T : class, IResource, IQuery where T : class, IResource, IQuery
{ {
/// <summary> /// <summary>
/// The Entity Framework's Database handle. /// The Entity Framework's Database handle.
/// </summary> /// </summary>
@ -170,9 +170,7 @@ namespace Kyoo.Core.Controllers
{ {
T? ret = await GetOrDefault(filter, include, sortBy, reverse, afterId); T? ret = await GetOrDefault(filter, include, sortBy, reverse, afterId);
if (ret == null) if (ret == null)
throw new ItemNotFoundException( throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
$"No {typeof(T).Name} found with the given predicate."
);
return ret; return ret;
} }
@ -244,13 +242,7 @@ namespace Kyoo.Core.Controllers
Pagination? limit = default Pagination? limit = default
) )
{ {
IQueryable<T> query = await ApplyFilters( IQueryable<T> query = await ApplyFilters(Database.Set<T>(), filter, sort, limit, include);
Database.Set<T>(),
filter,
sort,
limit,
include
);
return await query.ToListAsync(); return await query.ToListAsync();
} }
@ -435,9 +427,8 @@ namespace Kyoo.Core.Controllers
protected virtual Task Validate(T resource) protected virtual Task Validate(T resource)
{ {
if ( if (
typeof(T) typeof(T).GetProperty(nameof(resource.Slug))!.GetCustomAttribute<ComputedAttribute>()
.GetProperty(nameof(resource.Slug))! != null
.GetCustomAttribute<ComputedAttribute>() != null
) )
return Task.CompletedTask; return Task.CompletedTask;
if (string.IsNullOrEmpty(resource.Slug)) if (string.IsNullOrEmpty(resource.Slug))
@ -446,9 +437,7 @@ namespace Kyoo.Core.Controllers
{ {
try try
{ {
MethodInfo? setter = typeof(T) MethodInfo? setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod();
.GetProperty(nameof(resource.Slug))!
.GetSetMethod();
if (setter != null) if (setter != null)
setter.Invoke(resource, new object[] { resource.Slug + '!' }); setter.Invoke(resource, new object[] { resource.Slug + '!' });
else else
@ -495,5 +484,4 @@ namespace Kyoo.Core.Controllers
foreach (T resource in await GetAll(filter)) foreach (T resource in await GetAll(filter))
await Delete(resource); await Delete(resource);
} }
}
} }

View File

@ -25,13 +25,13 @@ using Kyoo.Abstractions.Models.Utils;
using Kyoo.Postgresql; using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle shows
/// </summary>
public class MovieRepository : LocalRepository<Movie>
{ {
/// <summary>
/// A local repository to handle shows
/// </summary>
public class MovieRepository : LocalRepository<Movie>
{
/// <summary> /// <summary>
/// The database handle /// The database handle
/// </summary> /// </summary>
@ -105,5 +105,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
await base.Delete(obj); await base.Delete(obj);
} }
}
} }

View File

@ -22,13 +22,13 @@ using System.Data.Common;
using System.IO; using System.IO;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle shows
/// </summary>
public class NewsRepository : DapperRepository<INews>
{ {
/// <summary>
/// A local repository to handle shows
/// </summary>
public class NewsRepository : DapperRepository<INews>
{
// language=PostgreSQL // language=PostgreSQL
protected override FormattableString Sql => protected override FormattableString Sql =>
$""" $"""
@ -60,5 +60,4 @@ namespace Kyoo.Core.Controllers
public NewsRepository(DbConnection database, SqlVariableContext context) public NewsRepository(DbConnection database, SqlVariableContext context)
: base(database, context) { } : base(database, context) { }
}
} }

View File

@ -29,13 +29,13 @@ using Kyoo.Postgresql;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle seasons.
/// </summary>
public class SeasonRepository : LocalRepository<Season>
{ {
/// <summary>
/// A local repository to handle seasons.
/// </summary>
public class SeasonRepository : LocalRepository<Season>
{
/// <summary> /// <summary>
/// The database handle /// The database handle
/// </summary> /// </summary>
@ -47,8 +47,7 @@ namespace Kyoo.Core.Controllers
IRepository<Show>.OnEdited += async (show) => IRepository<Show>.OnEdited += async (show) =>
{ {
await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope(); await using AsyncServiceScope scope = CoreModule.Services.CreateAsyncScope();
DatabaseContext database = DatabaseContext database = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
scope.ServiceProvider.GetRequiredService<DatabaseContext>();
List<Season> seasons = await database List<Season> seasons = await database
.Seasons.AsTracking() .Seasons.AsTracking()
.Where(x => x.ShowId == show.Id) .Where(x => x.ShowId == show.Id)
@ -129,5 +128,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
await base.Delete(obj); await base.Delete(obj);
} }
}
} }

View File

@ -26,13 +26,13 @@ using Kyoo.Postgresql;
using Kyoo.Utils; using Kyoo.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle shows
/// </summary>
public class ShowRepository : LocalRepository<Show>
{ {
/// <summary>
/// A local repository to handle shows
/// </summary>
public class ShowRepository : LocalRepository<Show>
{
/// <summary> /// <summary>
/// The database handle /// The database handle
/// </summary> /// </summary>
@ -106,5 +106,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
await base.Delete(obj); await base.Delete(obj);
} }
}
} }

View File

@ -26,13 +26,13 @@ using Kyoo.Postgresql;
using Kyoo.Utils; using Kyoo.Utils;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
/// <summary>
/// A local repository to handle studios
/// </summary>
public class StudioRepository : LocalRepository<Studio>
{ {
/// <summary>
/// A local repository to handle studios
/// </summary>
public class StudioRepository : LocalRepository<Studio>
{
/// <summary> /// <summary>
/// The database handle /// The database handle
/// </summary> /// </summary>
@ -78,5 +78,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
await base.Delete(obj); await base.Delete(obj);
} }
}
} }

View File

@ -31,19 +31,18 @@ using Kyoo.Abstractions.Models.Exceptions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using SkiaSharp; using SkiaSharp;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers;
{
/// <summary> /// <summary>
/// Download images and retrieve the path of those images for a resource. /// Download images and retrieve the path of those images for a resource.
/// </summary> /// </summary>
public class ThumbnailsManager( public class ThumbnailsManager(
IHttpClientFactory clientFactory, IHttpClientFactory clientFactory,
ILogger<ThumbnailsManager> logger, ILogger<ThumbnailsManager> logger,
Lazy<IRepository<User>> users Lazy<IRepository<User>> users
) : IThumbnailsManager ) : IThumbnailsManager
{ {
private static readonly Dictionary<string, TaskCompletionSource<object>> _downloading = private static readonly Dictionary<string, TaskCompletionSource<object>> _downloading = new();
new();
private static async Task _WriteTo(SKBitmap bitmap, string path, int quality) private static async Task _WriteTo(SKBitmap bitmap, string path, int quality)
{ {
@ -264,5 +263,4 @@ namespace Kyoo.Core.Controllers
Directory.CreateDirectory("/metadata/user"); Directory.CreateDirectory("/metadata/user");
await _WriteTo(ret, $"/metadata/user/{userId}.webp", 75); await _WriteTo(ret, $"/metadata/user/{userId}.webp", 75);
} }
}
} }

View File

@ -33,13 +33,13 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core namespace Kyoo.Core;
/// <summary>
/// The core module containing default implementations
/// </summary>
public class CoreModule : IPlugin
{ {
/// <summary>
/// The core module containing default implementations
/// </summary>
public class CoreModule : IPlugin
{
/// <summary> /// <summary>
/// A service provider to access services in static context (in events for example). /// A service provider to access services in static context (in events for example).
/// </summary> /// </summary>
@ -144,5 +144,4 @@ namespace Kyoo.Core
SA.Endpoint SA.Endpoint
) )
}; };
}
} }

View File

@ -25,17 +25,17 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Kyoo.Core namespace Kyoo.Core;
/// <summary>
/// A middleware to handle errors globally.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="ExceptionFilter"/> class.
/// </remarks>
/// <param name="logger">The logger used to log errors.</param>
public class ExceptionFilter(ILogger<ExceptionFilter> logger) : IExceptionFilter
{ {
/// <summary>
/// A middleware to handle errors globally.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="ExceptionFilter"/> class.
/// </remarks>
/// <param name="logger">The logger used to log errors.</param>
public class ExceptionFilter(ILogger<ExceptionFilter> logger) : IExceptionFilter
{
/// <inheritdoc/> /// <inheritdoc/>
public void OnException(ExceptionContext context) public void OnException(ExceptionContext context)
{ {
@ -79,5 +79,4 @@ namespace Kyoo.Core
StatusCode = StatusCodes.Status500InternalServerError; StatusCode = StatusCodes.Status500InternalServerError;
} }
} }
}
} }

View File

@ -22,16 +22,16 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// An API endpoint to check the health.
/// </summary>
[Route("health")]
[ApiController]
[ApiDefinition("Health")]
public class Health : BaseApi
{ {
/// <summary>
/// An API endpoint to check the health.
/// </summary>
[Route("health")]
[ApiController]
[ApiDefinition("Health")]
public class Health : BaseApi
{
private readonly HealthCheckService _healthCheckService; private readonly HealthCheckService _healthCheckService;
/// <summary> /// <summary>
@ -71,5 +71,4 @@ namespace Kyoo.Core.Api
/// The result of a health operation. /// The result of a health operation.
/// </summary> /// </summary>
public record HealthResult(string Status); public record HealthResult(string Status);
}
} }

View File

@ -25,13 +25,13 @@ using Kyoo.Abstractions.Models;
using Kyoo.Utils; using Kyoo.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// A common API containing custom methods to help inheritors.
/// </summary>
public abstract class BaseApi : ControllerBase
{ {
/// <summary>
/// A common API containing custom methods to help inheritors.
/// </summary>
public abstract class BaseApi : ControllerBase
{
/// <summary> /// <summary>
/// Construct and return a page from an api. /// Construct and return a page from an api.
/// </summary> /// </summary>
@ -96,5 +96,4 @@ namespace Kyoo.Core.Api
return new SearchPage<TResult>(result, self, previous, next, first); return new SearchPage<TResult>(result, self, previous, next, first);
} }
}
} }

View File

@ -27,16 +27,16 @@ using Kyoo.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
{
/// <summary> /// <summary>
/// A base class to handle CRUD operations on a specific resource type <typeparamref name="T"/>. /// A base class to handle CRUD operations on a specific resource type <typeparamref name="T"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of resource to make CRUD apis for.</typeparam> /// <typeparam name="T">The type of resource to make CRUD apis for.</typeparam>
[ApiController] [ApiController]
public class CrudApi<T> : BaseApi public class CrudApi<T> : BaseApi
where T : class, IResource, IQuery where T : class, IResource, IQuery
{ {
/// <summary> /// <summary>
/// The repository of the resource, used to retrieve, save and do operations on the baking store. /// The repository of the resource, used to retrieve, save and do operations on the baking store.
/// </summary> /// </summary>
@ -67,10 +67,7 @@ namespace Kyoo.Core.Api
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<T>> Get( public async Task<ActionResult<T>> Get(Identifier identifier, [FromQuery] Include<T>? fields)
Identifier identifier,
[FromQuery] Include<T>? fields
)
{ {
T? ret = await identifier.Match( T? ret = await identifier.Match(
id => Repository.GetOrDefault(id, fields), id => Repository.GetOrDefault(id, fields),
@ -272,5 +269,4 @@ namespace Kyoo.Core.Api
await Repository.DeleteAll(filter); await Repository.DeleteAll(filter);
return NoContent(); return NoContent();
} }
}
} }

View File

@ -26,17 +26,17 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
{
/// <summary> /// <summary>
/// A base class to handle CRUD operations and services thumbnails for /// A base class to handle CRUD operations and services thumbnails for
/// a specific resource type <typeparamref name="T"/>. /// a specific resource type <typeparamref name="T"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of resource to make CRUD and thumbnails apis for.</typeparam> /// <typeparam name="T">The type of resource to make CRUD and thumbnails apis for.</typeparam>
[ApiController] [ApiController]
public class CrudThumbsApi<T> : CrudApi<T> public class CrudThumbsApi<T> : CrudApi<T>
where T : class, IResource, IThumbnails, IQuery where T : class, IResource, IThumbnails, IQuery
{ {
/// <summary> /// <summary>
/// The thumbnail manager used to retrieve images paths. /// The thumbnail manager used to retrieve images paths.
/// </summary> /// </summary>
@ -97,10 +97,7 @@ namespace Kyoo.Core.Api
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public Task<IActionResult> GetPoster( public Task<IActionResult> GetPoster(Identifier identifier, [FromQuery] ImageQuality? quality)
Identifier identifier,
[FromQuery] ImageQuality? quality
)
{ {
return _GetImage(identifier, "poster", quality); return _GetImage(identifier, "poster", quality);
} }
@ -140,12 +137,8 @@ namespace Kyoo.Core.Api
/// </response> /// </response>
[HttpGet("{identifier:id}/thumbnail")] [HttpGet("{identifier:id}/thumbnail")]
[HttpGet("{identifier:id}/backdrop", Order = AlternativeRoute)] [HttpGet("{identifier:id}/backdrop", Order = AlternativeRoute)]
public Task<IActionResult> GetBackdrop( public Task<IActionResult> GetBackdrop(Identifier identifier, [FromQuery] ImageQuality? quality)
Identifier identifier,
[FromQuery] ImageQuality? quality
)
{ {
return _GetImage(identifier, "thumbnail", quality); return _GetImage(identifier, "thumbnail", quality);
} }
}
} }

View File

@ -28,18 +28,18 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// Information about one or multiple <see cref="Studio"/>.
/// </summary>
[Route("studios")]
[Route("studio", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Show))]
[ApiDefinition("Studios", Group = MetadataGroup)]
public class StudioApi : CrudApi<Studio>
{ {
/// <summary>
/// Information about one or multiple <see cref="Studio"/>.
/// </summary>
[Route("studios")]
[Route("studio", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Show))]
[ApiDefinition("Studios", Group = MetadataGroup)]
public class StudioApi : CrudApi<Studio>
{
/// <summary> /// <summary>
/// The library manager used to modify or retrieve information in the data store. /// The library manager used to modify or retrieve information in the data store.
/// </summary> /// </summary>
@ -99,5 +99,4 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
return Page(resources, pagination.Limit); return Page(resources, pagination.Limit);
} }
}
} }

View File

@ -30,18 +30,18 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// Information about one or multiple <see cref="Collection"/>.
/// </summary>
[Route("collections")]
[Route("collection", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Collection))]
[ApiDefinition("Collections", Group = ResourcesGroup)]
public class CollectionApi : CrudThumbsApi<Collection>
{ {
/// <summary>
/// Information about one or multiple <see cref="Collection"/>.
/// </summary>
[Route("collections")]
[Route("collection", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Collection))]
[ApiDefinition("Collections", Group = ResourcesGroup)]
public class CollectionApi : CrudThumbsApi<Collection>
{
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly CollectionRepository _collections; private readonly CollectionRepository _collections;
private readonly LibraryItemRepository _items; private readonly LibraryItemRepository _items;
@ -259,5 +259,4 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
return Page(resources, pagination.Limit); return Page(resources, pagination.Limit);
} }
}
} }

View File

@ -28,19 +28,19 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
{
/// <summary> /// <summary>
/// Information about one or multiple <see cref="Episode"/>. /// Information about one or multiple <see cref="Episode"/>.
/// </summary> /// </summary>
[Route("episodes")] [Route("episodes")]
[Route("episode", Order = AlternativeRoute)] [Route("episode", Order = AlternativeRoute)]
[ApiController] [ApiController]
[PartialPermission(nameof(Episode))] [PartialPermission(nameof(Episode))]
[ApiDefinition("Episodes", Group = ResourcesGroup)] [ApiDefinition("Episodes", Group = ResourcesGroup)]
public class EpisodeApi(ILibraryManager libraryManager, IThumbnailsManager thumbnails) public class EpisodeApi(ILibraryManager libraryManager, IThumbnailsManager thumbnails)
: TranscoderApi<Episode>(libraryManager.Episodes, thumbnails) : TranscoderApi<Episode>(libraryManager.Episodes, thumbnails)
{ {
/// <summary> /// <summary>
/// Get episode's show /// Get episode's show
/// </summary> /// </summary>
@ -195,5 +195,4 @@ namespace Kyoo.Core.Api
); );
return (path, $"/episodes/{identifier}"); return (path, $"/episodes/{identifier}");
} }
}
} }

View File

@ -23,19 +23,19 @@ using Kyoo.Abstractions.Models.Permissions;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// Endpoint for items that are not part of a specific library.
/// An item can ether represent a collection or a show.
/// </summary>
[Route("items")]
[Route("item", Order = AlternativeRoute)]
[ApiController]
[PartialPermission("LibraryItem")]
[ApiDefinition("Items", Group = ResourcesGroup)]
public class LibraryItemApi : CrudThumbsApi<ILibraryItem>
{ {
/// <summary>
/// Endpoint for items that are not part of a specific library.
/// An item can ether represent a collection or a show.
/// </summary>
[Route("items")]
[Route("item", Order = AlternativeRoute)]
[ApiController]
[PartialPermission("LibraryItem")]
[ApiDefinition("Items", Group = ResourcesGroup)]
public class LibraryItemApi : CrudThumbsApi<ILibraryItem>
{
/// <summary> /// <summary>
/// The library item repository used to modify or retrieve information in the data store. /// The library item repository used to modify or retrieve information in the data store.
/// </summary> /// </summary>
@ -53,5 +53,4 @@ namespace Kyoo.Core.Api
{ {
_libraryItems = libraryItems; _libraryItems = libraryItems;
} }
}
} }

View File

@ -30,19 +30,19 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
{
/// <summary> /// <summary>
/// Information about one or multiple <see cref="Movie"/>. /// Information about one or multiple <see cref="Movie"/>.
/// </summary> /// </summary>
[Route("movies")] [Route("movies")]
[Route("movie", Order = AlternativeRoute)] [Route("movie", Order = AlternativeRoute)]
[ApiController] [ApiController]
[PartialPermission(nameof(Show))] [PartialPermission(nameof(Show))]
[ApiDefinition("Shows", Group = ResourcesGroup)] [ApiDefinition("Shows", Group = ResourcesGroup)]
public class MovieApi(ILibraryManager libraryManager, IThumbnailsManager thumbs) public class MovieApi(ILibraryManager libraryManager, IThumbnailsManager thumbs)
: TranscoderApi<Movie>(libraryManager.Movies, thumbs) : TranscoderApi<Movie>(libraryManager.Movies, thumbs)
{ {
/// <summary> /// <summary>
/// Get studio that made the show /// Get studio that made the show
/// </summary> /// </summary>
@ -207,5 +207,4 @@ namespace Kyoo.Core.Api
); );
return (path, $"/movies/{identifier}"); return (path, $"/movies/{identifier}");
} }
}
} }

View File

@ -23,19 +23,18 @@ using Kyoo.Abstractions.Models.Permissions;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// List new items added to kyoo.
/// </summary>
[Route("news")]
[Route("new", Order = AlternativeRoute)]
[ApiController]
[PartialPermission("LibraryItem")]
[ApiDefinition("News", Group = ResourcesGroup)]
public class NewsApi : CrudThumbsApi<INews>
{ {
/// <summary>
/// List new items added to kyoo.
/// </summary>
[Route("news")]
[Route("new", Order = AlternativeRoute)]
[ApiController]
[PartialPermission("LibraryItem")]
[ApiDefinition("News", Group = ResourcesGroup)]
public class NewsApi : CrudThumbsApi<INews>
{
public NewsApi(IRepository<INews> news, IThumbnailsManager thumbs) public NewsApi(IRepository<INews> news, IThumbnailsManager thumbs)
: base(news, thumbs) { } : base(news, thumbs) { }
}
} }

View File

@ -26,17 +26,17 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// An endpoint to search for every resources of kyoo. Searching for only a specific type of resource
/// is available on the said endpoint.
/// </summary>
[Route("search")]
[ApiController]
[ApiDefinition("Search", Group = ResourcesGroup)]
public class SearchApi : BaseApi
{ {
/// <summary>
/// An endpoint to search for every resources of kyoo. Searching for only a specific type of resource
/// is available on the said endpoint.
/// </summary>
[Route("search")]
[ApiController]
[ApiDefinition("Search", Group = ResourcesGroup)]
public class SearchApi : BaseApi
{
private readonly ISearchManager _searchManager; private readonly ISearchManager _searchManager;
public SearchApi(ISearchManager searchManager) public SearchApi(ISearchManager searchManager)
@ -69,9 +69,7 @@ namespace Kyoo.Core.Api
[FromQuery] Include<Collection> fields [FromQuery] Include<Collection> fields
) )
{ {
return SearchPage( return SearchPage(await _searchManager.SearchCollections(q, sortBy, pagination, fields));
await _searchManager.SearchCollections(q, sortBy, pagination, fields)
);
} }
/// <summary> /// <summary>
@ -203,5 +201,4 @@ namespace Kyoo.Core.Api
{ {
return SearchPage(await _searchManager.SearchStudios(q, sortBy, pagination, fields)); return SearchPage(await _searchManager.SearchStudios(q, sortBy, pagination, fields));
} }
}
} }

View File

@ -28,18 +28,18 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// Information about one or multiple <see cref="Season"/>.
/// </summary>
[Route("seasons")]
[Route("season", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Season))]
[ApiDefinition("Seasons", Group = ResourcesGroup)]
public class SeasonApi : CrudThumbsApi<Season>
{ {
/// <summary>
/// Information about one or multiple <see cref="Season"/>.
/// </summary>
[Route("seasons")]
[Route("season", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Season))]
[ApiDefinition("Seasons", Group = ResourcesGroup)]
public class SeasonApi : CrudThumbsApi<Season>
{
/// <summary> /// <summary>
/// The library manager used to modify or retrieve information in the data store. /// The library manager used to modify or retrieve information in the data store.
/// </summary> /// </summary>
@ -87,10 +87,7 @@ namespace Kyoo.Core.Api
) )
{ {
ICollection<Episode> resources = await _libraryManager.Episodes.GetAll( ICollection<Episode> resources = await _libraryManager.Episodes.GetAll(
Filter.And( Filter.And(filter, identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season!.Slug)),
filter,
identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season!.Slug)
),
sortBy, sortBy,
fields, fields,
pagination pagination
@ -131,5 +128,4 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
return ret; return ret;
} }
}
} }

View File

@ -30,18 +30,18 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// Information about one or multiple <see cref="Show"/>.
/// </summary>
[Route("shows")]
[Route("show", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Show))]
[ApiDefinition("Shows", Group = ResourcesGroup)]
public class ShowApi : CrudThumbsApi<Show>
{ {
/// <summary>
/// Information about one or multiple <see cref="Show"/>.
/// </summary>
[Route("shows")]
[Route("show", Order = AlternativeRoute)]
[ApiController]
[PartialPermission(nameof(Show))]
[ApiDefinition("Shows", Group = ResourcesGroup)]
public class ShowApi : CrudThumbsApi<Show>
{
/// <summary> /// <summary>
/// The library manager used to modify or retrieve information in the data store. /// The library manager used to modify or retrieve information in the data store.
/// </summary> /// </summary>
@ -256,10 +256,7 @@ namespace Kyoo.Core.Api
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ShowWatchStatus?> SetWatchStatus( public async Task<ShowWatchStatus?> SetWatchStatus(Identifier identifier, WatchStatus status)
Identifier identifier,
WatchStatus status
)
{ {
Guid id = await identifier.Match( Guid id = await identifier.Match(
id => Task.FromResult(id), id => Task.FromResult(id),
@ -290,5 +287,4 @@ namespace Kyoo.Core.Api
); );
await _libraryManager.WatchStatus.DeleteShowStatus(id, User.GetIdOrThrow()); await _libraryManager.WatchStatus.DeleteShowStatus(id, User.GetIdOrThrow());
} }
}
} }

View File

@ -29,18 +29,18 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// List new items added to kyoo.
/// </summary>
[Route("watchlist")]
[ApiController]
[PartialPermission("LibraryItem")]
[ApiDefinition("News", Group = ResourcesGroup)]
[UserOnly]
public class WatchlistApi(IWatchStatusRepository repository) : BaseApi
{ {
/// <summary>
/// List new items added to kyoo.
/// </summary>
[Route("watchlist")]
[ApiController]
[PartialPermission("LibraryItem")]
[ApiDefinition("News", Group = ResourcesGroup)]
[UserOnly]
public class WatchlistApi(IWatchStatusRepository repository) : BaseApi
{
/// <summary> /// <summary>
/// Get all /// Get all
/// </summary> /// </summary>
@ -68,5 +68,4 @@ namespace Kyoo.Core.Api
return Page(resources, pagination.Limit); return Page(resources, pagination.Limit);
} }
}
} }

View File

@ -28,15 +28,15 @@ using Kyoo.Utils;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api;
/// <summary>
/// Proxy to other services
/// </summary>
[ApiController]
[Obsolete("Use /episode/id/master.m3u8 or routes like that")]
public class ProxyApi(ILibraryManager library) : Controller
{ {
/// <summary>
/// Proxy to other services
/// </summary>
[ApiController]
[Obsolete("Use /episode/id/master.m3u8 or routes like that")]
public class ProxyApi(ILibraryManager library) : Controller
{
private Task _Proxy(string route, (string path, string route) info) private Task _Proxy(string route, (string path, string route) info)
{ {
HttpProxyOptions proxyOptions = HttpProxyOptionsBuilder HttpProxyOptions proxyOptions = HttpProxyOptionsBuilder
@ -92,5 +92,4 @@ namespace Kyoo.Core.Api
); );
await _Proxy(rest + query.ToQueryString(), (path, $"{type}/{id}")); await _Proxy(rest + query.ToQueryString(), (path, $"{type}/{id}"));
} }
}
} }

View File

@ -37,14 +37,14 @@ using Serilog.Templates;
using Serilog.Templates.Themes; using Serilog.Templates.Themes;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
namespace Kyoo.Host namespace Kyoo.Host;
/// <summary>
/// Hosts of kyoo (main functions) generally only create a new <see cref="Application"/>
/// and return <see cref="Start(string[])"/>.
/// </summary>
public class Application
{ {
/// <summary>
/// Hosts of kyoo (main functions) generally only create a new <see cref="Application"/>
/// and return <see cref="Start(string[])"/>.
/// </summary>
public class Application
{
/// <summary> /// <summary>
/// The environment in witch Kyoo will run (ether "Production" or "Development"). /// The environment in witch Kyoo will run (ether "Production" or "Development").
/// </summary> /// </summary>
@ -124,10 +124,7 @@ namespace Kyoo.Host
"Version: {Version}", "Version: {Version}",
Assembly.GetExecutingAssembly().GetName().Version.ToString(3) Assembly.GetExecutingAssembly().GetName().Version.ToString(3)
); );
_logger.Information( _logger.Information("Data directory: {DataDirectory}", Environment.CurrentDirectory);
"Data directory: {DataDirectory}",
Environment.CurrentDirectory
);
await host.RunAsync(cancellationToken); await host.RunAsync(cancellationToken);
} }
catch (Exception ex) catch (Exception ex)
@ -157,9 +154,7 @@ namespace Kyoo.Host
}) })
.UseIIS() .UseIIS()
.UseIISIntegration() .UseIISIntegration()
.UseUrls( .UseUrls(Environment.GetEnvironmentVariable("KYOO_BIND_URL") ?? "http://*:5000")
Environment.GetEnvironmentVariable("KYOO_BIND_URL") ?? "http://*:5000"
)
.UseStartup(host => .UseStartup(host =>
PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog()) PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog())
) )
@ -198,5 +193,4 @@ namespace Kyoo.Host
.Enrich.WithThreadId() .Enrich.WithThreadId()
.Enrich.FromLogContext(); .Enrich.FromLogContext();
} }
}
} }

View File

@ -23,14 +23,14 @@ using Kyoo.Abstractions.Controllers;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Kyoo.Host.Controllers namespace Kyoo.Host.Controllers;
/// <summary>
/// An implementation of <see cref="IPluginManager"/>.
/// This is used to load plugins and retrieve information from them.
/// </summary>
public class PluginManager : IPluginManager
{ {
/// <summary>
/// An implementation of <see cref="IPluginManager"/>.
/// This is used to load plugins and retrieve information from them.
/// </summary>
public class PluginManager : IPluginManager
{
/// <summary> /// <summary>
/// The service provider. It allow plugin's activation. /// The service provider. It allow plugin's activation.
/// </summary> /// </summary>
@ -86,10 +86,7 @@ namespace Kyoo.Host.Controllers
public void LoadPlugins(params Type[] plugins) public void LoadPlugins(params Type[] plugins)
{ {
LoadPlugins( LoadPlugins(
plugins plugins.Select(x => (IPlugin)ActivatorUtilities.CreateInstance(_provider, x)).ToArray()
.Select(x => (IPlugin)ActivatorUtilities.CreateInstance(_provider, x))
.ToArray()
); );
} }
}
} }

View File

@ -23,13 +23,13 @@ using Kyoo.Abstractions.Controllers;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Serilog; using Serilog;
namespace Kyoo.Host namespace Kyoo.Host;
/// <summary>
/// A module that registers host controllers and other needed things.
/// </summary>
public class HostModule : IPlugin
{ {
/// <summary>
/// A module that registers host controllers and other needed things.
/// </summary>
public class HostModule : IPlugin
{
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Host"; public string Name => "Host";
@ -57,5 +57,4 @@ namespace Kyoo.Host
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps => public IEnumerable<IStartupAction> ConfigureSteps =>
new[] { SA.New<IApplicationBuilder>(app => app.UseSerilogRequestLogging(), SA.Before) }; new[] { SA.New<IApplicationBuilder>(app => app.UseSerilogRequestLogging(), SA.Before) };
}
} }

View File

@ -36,13 +36,13 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Kyoo.Host namespace Kyoo.Host;
/// <summary>
/// The Startup class is used to configure the AspNet's webhost.
/// </summary>
public class PluginsStartup
{ {
/// <summary>
/// The Startup class is used to configure the AspNet's webhost.
/// </summary>
public class PluginsStartup
{
/// <summary> /// <summary>
/// A plugin manager used to load plugins and allow them to configure services / asp net. /// A plugin manager used to load plugins and allow them to configure services / asp net.
/// </summary> /// </summary>
@ -81,8 +81,7 @@ namespace Kyoo.Host
/// <returns>A new <see cref="PluginsStartup"/>.</returns> /// <returns>A new <see cref="PluginsStartup"/>.</returns>
public static PluginsStartup FromWebHost(WebHostBuilderContext host, ILoggerFactory logger) public static PluginsStartup FromWebHost(WebHostBuilderContext host, ILoggerFactory logger)
{ {
HostServiceProvider hostProvider = HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger);
new(host.HostingEnvironment, host.Configuration, logger);
PluginManager plugins = new(hostProvider, logger.CreateLogger<PluginManager>()); PluginManager plugins = new(hostProvider, logger.CreateLogger<PluginManager>());
return new PluginsStartup(plugins); return new PluginsStartup(plugins);
} }
@ -93,9 +92,7 @@ namespace Kyoo.Host
/// <param name="services">The service collection to fill.</param> /// <param name="services">The service collection to fill.</param>
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
foreach ( foreach (Assembly assembly in _plugins.GetAllPlugins().Select(x => x.GetType().Assembly))
Assembly assembly in _plugins.GetAllPlugins().Select(x => x.GetType().Assembly)
)
services.AddMvcCore().AddApplicationPart(assembly); services.AddMvcCore().AddApplicationPart(assembly);
_hostModule.Configure(services); _hostModule.Configure(services);
@ -198,5 +195,4 @@ namespace Kyoo.Host
return null; return null;
} }
} }
}
} }

View File

@ -19,13 +19,13 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
namespace Kyoo.Host namespace Kyoo.Host;
/// <summary>
/// Program entrypoint.
/// </summary>
public static class Program
{ {
/// <summary>
/// Program entrypoint.
/// </summary>
public static class Program
{
/// <summary> /// <summary>
/// The string representation of the environment used in <see cref="IWebHostEnvironment"/>. /// The string representation of the environment used in <see cref="IWebHostEnvironment"/>.
/// </summary> /// </summary>
@ -45,5 +45,4 @@ namespace Kyoo.Host
Application application = new(Environment); Application application = new(Environment);
return application.Start(args); return application.Start(args);
} }
}
} }

View File

@ -24,10 +24,10 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using static System.Text.Json.JsonNamingPolicy; using static System.Text.Json.JsonNamingPolicy;
namespace Kyoo.Meiliseach namespace Kyoo.Meiliseach;
public class MeilisearchModule : IPlugin
{ {
public class MeilisearchModule : IPlugin
{
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Meilisearch"; public string Name => "Meilisearch";
@ -64,11 +64,7 @@ namespace Kyoo.Meiliseach
CamelCase.ConvertName(nameof(Movie.Rating)), CamelCase.ConvertName(nameof(Movie.Rating)),
CamelCase.ConvertName(nameof(Movie.Runtime)), CamelCase.ConvertName(nameof(Movie.Runtime)),
}, },
DisplayedAttributes = new[] DisplayedAttributes = new[] { CamelCase.ConvertName(nameof(Movie.Id)), "kind" },
{
CamelCase.ConvertName(nameof(Movie.Id)),
"kind"
},
RankingRules = new[] RankingRules = new[]
{ {
"words", "words",
@ -190,5 +186,4 @@ namespace Kyoo.Meiliseach
builder.RegisterType<MeiliSync>().AsSelf().SingleInstance().AutoActivate(); builder.RegisterType<MeiliSync>().AsSelf().SingleInstance().AutoActivate();
builder.RegisterType<SearchManager>().As<ISearchManager>().InstancePerLifetimeScope(); builder.RegisterType<SearchManager>().As<ISearchManager>().InstancePerLifetimeScope();
} }
}
} }

View File

@ -31,17 +31,17 @@ using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Kyoo.Postgresql namespace Kyoo.Postgresql;
/// <summary>
/// The database handle used for all local repositories.
/// This is an abstract class. It is meant to be implemented by plugins. This allow the core to be database agnostic.
/// </summary>
/// <remarks>
/// It should not be used directly, to access the database use a <see cref="ILibraryManager"/> or repositories.
/// </remarks>
public abstract class DatabaseContext : DbContext
{ {
/// <summary>
/// The database handle used for all local repositories.
/// This is an abstract class. It is meant to be implemented by plugins. This allow the core to be database agnostic.
/// </summary>
/// <remarks>
/// It should not be used directly, to access the database use a <see cref="ILibraryManager"/> or repositories.
/// </remarks>
public abstract class DatabaseContext : DbContext
{
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
/// <summary> /// <summary>
@ -260,10 +260,7 @@ namespace Kyoo.Postgresql
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Show>().Ignore(x => x.FirstEpisode).Ignore(x => x.AirDate); modelBuilder.Entity<Show>().Ignore(x => x.FirstEpisode).Ignore(x => x.AirDate);
modelBuilder modelBuilder.Entity<Episode>().Ignore(x => x.PreviousEpisode).Ignore(x => x.NextEpisode);
.Entity<Episode>()
.Ignore(x => x.PreviousEpisode)
.Ignore(x => x.NextEpisode);
modelBuilder modelBuilder
.Entity<Show>() .Entity<Show>()
@ -353,9 +350,7 @@ namespace Kyoo.Postgresql
modelBuilder.Entity<MovieWatchStatus>().HasQueryFilter(x => x.UserId == CurrentUserId); modelBuilder.Entity<MovieWatchStatus>().HasQueryFilter(x => x.UserId == CurrentUserId);
modelBuilder.Entity<ShowWatchStatus>().HasQueryFilter(x => x.UserId == CurrentUserId); modelBuilder.Entity<ShowWatchStatus>().HasQueryFilter(x => x.UserId == CurrentUserId);
modelBuilder modelBuilder.Entity<EpisodeWatchStatus>().HasQueryFilter(x => x.UserId == CurrentUserId);
.Entity<EpisodeWatchStatus>()
.HasQueryFilter(x => x.UserId == CurrentUserId);
modelBuilder.Entity<ShowWatchStatus>().Navigation(x => x.NextEpisode).AutoInclude(); modelBuilder.Entity<ShowWatchStatus>().Navigation(x => x.NextEpisode).AutoInclude();
@ -543,9 +538,7 @@ namespace Kyoo.Postgresql
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception> /// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
/// <returns>The number of state entries written to the database.</returns> /// <returns>The number of state entries written to the database.</returns>
public override async Task<int> SaveChangesAsync( public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default
)
{ {
try try
{ {
@ -635,13 +628,10 @@ namespace Kyoo.Postgresql
public void DiscardChanges() public void DiscardChanges()
{ {
foreach ( foreach (
EntityEntry entry in ChangeTracker EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Detached)
.Entries()
.Where(x => x.State != EntityState.Detached)
) )
{ {
entry.State = EntityState.Detached; entry.State = EntityState.Detached;
} }
} }
}
} }

View File

@ -25,13 +25,13 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Npgsql; using Npgsql;
namespace Kyoo.Postgresql namespace Kyoo.Postgresql;
/// <summary>
/// A postgresql implementation of <see cref="DatabaseContext"/>.
/// </summary>
public class PostgresContext : DatabaseContext
{ {
/// <summary>
/// A postgresql implementation of <see cref="DatabaseContext"/>.
/// </summary>
public class PostgresContext : DatabaseContext
{
/// <summary> /// <summary>
/// Is this instance in debug mode? /// Is this instance in debug mode?
/// </summary> /// </summary>
@ -134,5 +134,4 @@ namespace Kyoo.Postgresql
or PostgresErrorCodes.ForeignKeyViolation or PostgresErrorCodes.ForeignKeyViolation
}; };
} }
}
} }

View File

@ -33,13 +33,13 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Npgsql; using Npgsql;
namespace Kyoo.Postgresql namespace Kyoo.Postgresql;
/// <summary>
/// A module to add postgresql capacity to the app.
/// </summary>
public class PostgresModule : IPlugin
{ {
/// <summary>
/// A module to add postgresql capacity to the app.
/// </summary>
public class PostgresModule : IPlugin
{
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Postgresql"; public string Name => "Postgresql";
@ -137,11 +137,8 @@ namespace Kyoo.Postgresql
}, },
ServiceLifetime.Transient ServiceLifetime.Transient
); );
services.AddTransient<DbConnection>( services.AddTransient<DbConnection>((_) => new NpgsqlConnection(builder.ConnectionString));
(_) => new NpgsqlConnection(builder.ConnectionString)
);
services.AddHealthChecks().AddDbContextCheck<DatabaseContext>(); services.AddHealthChecks().AddDbContextCheck<DatabaseContext>();
} }
}
} }

View File

@ -22,13 +22,13 @@ using Kyoo.Swagger.Models;
using NSwag; using NSwag;
using NSwag.Generation.AspNetCore; using NSwag.Generation.AspNetCore;
namespace Kyoo.Swagger namespace Kyoo.Swagger;
/// <summary>
/// A class to sort apis.
/// </summary>
public static class ApiSorter
{ {
/// <summary>
/// A class to sort apis.
/// </summary>
public static class ApiSorter
{
/// <summary> /// <summary>
/// Sort apis by alphabetical orders. /// Sort apis by alphabetical orders.
/// </summary> /// </summary>
@ -62,5 +62,4 @@ namespace Kyoo.Swagger
.ToList(); .ToList();
}; };
} }
}
} }

View File

@ -26,15 +26,15 @@ using NSwag;
using NSwag.Generation.AspNetCore; using NSwag.Generation.AspNetCore;
using NSwag.Generation.Processors.Contexts; using NSwag.Generation.Processors.Contexts;
namespace Kyoo.Swagger namespace Kyoo.Swagger;
/// <summary>
/// A class to handle Api Groups (OpenApi tags and x-tagGroups).
/// Tags should be specified via <see cref="ApiDefinitionAttribute"/> and this filter will map this to the
/// <see cref="OpenApiDocument"/>.
/// </summary>
public static class ApiTagsFilter
{ {
/// <summary>
/// A class to handle Api Groups (OpenApi tags and x-tagGroups).
/// Tags should be specified via <see cref="ApiDefinitionAttribute"/> and this filter will map this to the
/// <see cref="OpenApiDocument"/>.
/// </summary>
public static class ApiTagsFilter
{
/// <summary> /// <summary>
/// The main operation filter that will map every <see cref="ApiDefinitionAttribute"/>. /// The main operation filter that will map every <see cref="ApiDefinitionAttribute"/>.
/// </summary> /// </summary>
@ -120,5 +120,4 @@ namespace Kyoo.Swagger
options.AddOperationFilter(OperationFilter); options.AddOperationFilter(OperationFilter);
options.PostProcess += x => x.AddLeftoversToOthersGroup(); options.PostProcess += x => x.AddLeftoversToOthersGroup();
} }
}
} }

View File

@ -24,19 +24,19 @@ using Kyoo.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Kyoo.Swagger namespace Kyoo.Swagger;
/// <summary>
/// A filter that change <see cref="ProducesResponseTypeAttribute"/>'s
/// <see cref="ProducesResponseTypeAttribute.Type"/> that where set to <see cref="ActionResult{T}"/> to the
/// return type of the method.
/// </summary>
/// <remarks>
/// This is only useful when the return type of the method is a generics type and that can't be specified in the
/// attribute directly (since attributes don't support generics). This should not be used otherwise.
/// </remarks>
public class GenericResponseProvider : IApplicationModelProvider
{ {
/// <summary>
/// A filter that change <see cref="ProducesResponseTypeAttribute"/>'s
/// <see cref="ProducesResponseTypeAttribute.Type"/> that where set to <see cref="ActionResult{T}"/> to the
/// return type of the method.
/// </summary>
/// <remarks>
/// This is only useful when the return type of the method is a generics type and that can't be specified in the
/// attribute directly (since attributes don't support generics). This should not be used otherwise.
/// </remarks>
public class GenericResponseProvider : IApplicationModelProvider
{
/// <inheritdoc /> /// <inheritdoc />
public int Order => -1; public int Order => -1;
@ -65,5 +65,4 @@ namespace Kyoo.Swagger
} }
} }
} }
}
} }

View File

@ -20,13 +20,13 @@ using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using NSwag; using NSwag;
namespace Kyoo.Swagger.Models namespace Kyoo.Swagger.Models;
/// <summary>
/// A class representing a group of tags in the <see cref="OpenApiDocument"/>
/// </summary>
public class TagGroups
{ {
/// <summary>
/// A class representing a group of tags in the <see cref="OpenApiDocument"/>
/// </summary>
public class TagGroups
{
/// <summary> /// <summary>
/// The name of the tag group. /// The name of the tag group.
/// </summary> /// </summary>
@ -38,5 +38,4 @@ namespace Kyoo.Swagger.Models
/// </summary> /// </summary>
[JsonProperty(PropertyName = "tags")] [JsonProperty(PropertyName = "tags")]
public List<string> Tags { get; set; } public List<string> Tags { get; set; }
}
} }

View File

@ -25,19 +25,18 @@ using NSwag;
using NSwag.Generation.Processors; using NSwag.Generation.Processors;
using NSwag.Generation.Processors.Contexts; using NSwag.Generation.Processors.Contexts;
namespace Kyoo.Swagger namespace Kyoo.Swagger;
/// <summary>
/// An operation processor that adds permissions information from the <see cref="PermissionAttribute"/> and the
/// <see cref="PartialPermissionAttribute"/>.
/// </summary>
public class OperationPermissionProcessor : IOperationProcessor
{ {
/// <summary>
/// An operation processor that adds permissions information from the <see cref="PermissionAttribute"/> and the
/// <see cref="PartialPermissionAttribute"/>.
/// </summary>
public class OperationPermissionProcessor : IOperationProcessor
{
/// <inheritdoc /> /// <inheritdoc />
public bool Process(OperationProcessorContext context) public bool Process(OperationProcessorContext context)
{ {
context.OperationDescription.Operation.Security ??= context.OperationDescription.Operation.Security ??= new List<OpenApiSecurityRequirement>();
new List<OpenApiSecurityRequirement>();
OpenApiSecurityRequirement perms = context OpenApiSecurityRequirement perms = context
.MethodInfo.GetCustomAttributes<UserOnlyAttribute>() .MethodInfo.GetCustomAttributes<UserOnlyAttribute>()
.Aggregate( .Aggregate(
@ -100,5 +99,4 @@ namespace Kyoo.Swagger
? perms.ToList() ? perms.ToList()
: new List<string>(); : new List<string>();
} }
}
} }

View File

@ -29,13 +29,13 @@ using NSwag;
using NSwag.Generation.AspNetCore; using NSwag.Generation.AspNetCore;
using static Kyoo.Abstractions.Models.Utils.Constants; using static Kyoo.Abstractions.Models.Utils.Constants;
namespace Kyoo.Swagger namespace Kyoo.Swagger;
/// <summary>
/// A module to enable a swagger interface and an OpenAPI endpoint to document Kyoo.
/// </summary>
public class SwaggerModule : IPlugin
{ {
/// <summary>
/// A module to enable a swagger interface and an OpenAPI endpoint to document Kyoo.
/// </summary>
public class SwaggerModule : IPlugin
{
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Swagger"; public string Name => "Swagger";
@ -127,5 +127,4 @@ namespace Kyoo.Swagger
SA.Before SA.Before
) )
}; };
}
} }