CodingStlye: Fixing whitespaces

This commit is contained in:
Zoe Roux 2021-09-03 21:52:15 +02:00
parent 8ff2fe3965
commit d3a03771dd
152 changed files with 2138 additions and 2120 deletions

View File

@ -7,6 +7,7 @@ trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
indent_style = tab indent_style = tab
indent_size = tab indent_size = tab
smart_tab = true
[{*.yaml,*.yml}] [{*.yaml,*.yml}]
indent_style = space indent_style = space

View File

@ -6,12 +6,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.354" /> <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.354" PrivateAssets="All" />
<None Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" Visible="false" /> <None Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" Visible="false" />
<None Include="$(MSBuildThisFileDirectory).editorconfig" Link="stylecop.json" Visible="false" /> <None Include="$(MSBuildThisFileDirectory).editorconfig" Link=".editorconfig" Visible="false" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Kyoo.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Kyoo.ruleset</CodeAnalysisRuleSet>
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -26,62 +26,62 @@ namespace Kyoo.Abstractions.Controllers
/// The repository that handle libraries. /// The repository that handle libraries.
/// </summary> /// </summary>
ILibraryRepository LibraryRepository { get; } ILibraryRepository LibraryRepository { get; }
/// <summary> /// <summary>
/// The repository that handle libraries's items (a wrapper arround shows & collections). /// The repository that handle libraries's items (a wrapper arround shows & collections).
/// </summary> /// </summary>
ILibraryItemRepository LibraryItemRepository { get; } ILibraryItemRepository LibraryItemRepository { get; }
/// <summary> /// <summary>
/// The repository that handle collections. /// The repository that handle collections.
/// </summary> /// </summary>
ICollectionRepository CollectionRepository { get; } ICollectionRepository CollectionRepository { get; }
/// <summary> /// <summary>
/// The repository that handle shows. /// The repository that handle shows.
/// </summary> /// </summary>
IShowRepository ShowRepository { get; } IShowRepository ShowRepository { get; }
/// <summary> /// <summary>
/// The repository that handle seasons. /// The repository that handle seasons.
/// </summary> /// </summary>
ISeasonRepository SeasonRepository { get; } ISeasonRepository SeasonRepository { get; }
/// <summary> /// <summary>
/// The repository that handle episodes. /// The repository that handle episodes.
/// </summary> /// </summary>
IEpisodeRepository EpisodeRepository { get; } IEpisodeRepository EpisodeRepository { get; }
/// <summary> /// <summary>
/// The repository that handle tracks. /// The repository that handle tracks.
/// </summary> /// </summary>
ITrackRepository TrackRepository { get; } ITrackRepository TrackRepository { get; }
/// <summary> /// <summary>
/// The repository that handle people. /// The repository that handle people.
/// </summary> /// </summary>
IPeopleRepository PeopleRepository { get; } IPeopleRepository PeopleRepository { get; }
/// <summary> /// <summary>
/// The repository that handle studios. /// The repository that handle studios.
/// </summary> /// </summary>
IStudioRepository StudioRepository { get; } IStudioRepository StudioRepository { get; }
/// <summary> /// <summary>
/// The repository that handle genres. /// The repository that handle genres.
/// </summary> /// </summary>
IGenreRepository GenreRepository { get; } IGenreRepository GenreRepository { get; }
/// <summary> /// <summary>
/// The repository that handle providers. /// The repository that handle providers.
/// </summary> /// </summary>
IProviderRepository ProviderRepository { get; } IProviderRepository ProviderRepository { get; }
/// <summary> /// <summary>
/// The repository that handle users. /// The repository that handle users.
/// </summary> /// </summary>
IUserRepository UserRepository { get; } IUserRepository UserRepository { get; }
/// <summary> /// <summary>
/// Get the resource by it's ID /// Get the resource by it's ID
/// </summary> /// </summary>
@ -91,7 +91,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns> /// <returns>The resource found</returns>
[ItemNotNull] [ItemNotNull]
Task<T> Get<T>(int id) where T : class, IResource; Task<T> Get<T>(int id) where T : class, IResource;
/// <summary> /// <summary>
/// Get the resource by it's slug /// Get the resource by it's slug
/// </summary> /// </summary>
@ -101,7 +101,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns> /// <returns>The resource found</returns>
[ItemNotNull] [ItemNotNull]
Task<T> Get<T>(string slug) where T : class, IResource; Task<T> Get<T>(string slug) where T : class, IResource;
/// <summary> /// <summary>
/// Get the resource by a filter function. /// Get the resource by a filter function.
/// </summary> /// </summary>
@ -121,7 +121,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The season found</returns> /// <returns>The season found</returns>
[ItemNotNull] [ItemNotNull]
Task<Season> Get(int showID, int seasonNumber); Task<Season> Get(int showID, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's show slug and it's seasonNumber /// Get a season from it's show slug and it's seasonNumber
/// </summary> /// </summary>
@ -131,7 +131,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The season found</returns> /// <returns>The season found</returns>
[ItemNotNull] [ItemNotNull]
Task<Season> Get(string showSlug, int seasonNumber); Task<Season> Get(string showSlug, int seasonNumber);
/// <summary> /// <summary>
/// Get a episode from it's showID, it's seasonNumber and it's episode number. /// Get a episode from it's showID, it's seasonNumber and it's episode number.
/// </summary> /// </summary>
@ -142,7 +142,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The episode found</returns> /// <returns>The episode found</returns>
[ItemNotNull] [ItemNotNull]
Task<Episode> Get(int showID, int seasonNumber, int episodeNumber); Task<Episode> Get(int showID, int seasonNumber, int episodeNumber);
/// <summary> /// <summary>
/// Get a episode from it's show slug, it's seasonNumber and it's episode number. /// Get a episode from it's show slug, it's seasonNumber and it's episode number.
/// </summary> /// </summary>
@ -162,7 +162,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns> /// <returns>The resource found</returns>
[ItemCanBeNull] [ItemCanBeNull]
Task<T> GetOrDefault<T>(int id) where T : class, IResource; Task<T> GetOrDefault<T>(int id) where T : class, IResource;
/// <summary> /// <summary>
/// Get the resource by it's slug or null if it is not found. /// Get the resource by it's slug or null if it is not found.
/// </summary> /// </summary>
@ -171,7 +171,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns> /// <returns>The resource found</returns>
[ItemCanBeNull] [ItemCanBeNull]
Task<T> GetOrDefault<T>(string slug) where T : class, IResource; Task<T> GetOrDefault<T>(string slug) where T : class, IResource;
/// <summary> /// <summary>
/// Get the resource by a filter function or null if it is not found. /// Get the resource by a filter function or null if it is not found.
/// </summary> /// </summary>
@ -189,7 +189,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The season found</returns> /// <returns>The season found</returns>
[ItemCanBeNull] [ItemCanBeNull]
Task<Season> GetOrDefault(int showID, int seasonNumber); Task<Season> GetOrDefault(int showID, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's show slug and it's seasonNumber or null if it is not found. /// Get a season from it's show slug and it's seasonNumber or null if it is not found.
/// </summary> /// </summary>
@ -198,7 +198,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The season found</returns> /// <returns>The season found</returns>
[ItemCanBeNull] [ItemCanBeNull]
Task<Season> GetOrDefault(string showSlug, int seasonNumber); Task<Season> GetOrDefault(string showSlug, int seasonNumber);
/// <summary> /// <summary>
/// Get a episode from it's showID, it's seasonNumber and it's episode number or null if it is not found. /// Get a episode from it's showID, it's seasonNumber and it's episode number or null if it is not found.
/// </summary> /// </summary>
@ -208,7 +208,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The episode found</returns> /// <returns>The episode found</returns>
[ItemCanBeNull] [ItemCanBeNull]
Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber); Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
/// <summary> /// <summary>
/// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found. /// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found.
/// </summary> /// </summary>
@ -284,7 +284,7 @@ namespace Kyoo.Abstractions.Controllers
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}}, bool)"/> /// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}}, bool)"/>
/// <seealso cref="Load{T}(T, System.String, bool)"/> /// <seealso cref="Load{T}(T, System.String, bool)"/>
Task Load([NotNull] IResource obj, string memberName, bool force = false); Task Load([NotNull] IResource obj, string memberName, bool force = false);
/// <summary> /// <summary>
/// Get items (A wrapper arround shows or collections) from a library. /// Get items (A wrapper arround shows or collections) from a library.
/// </summary> /// </summary>
@ -297,7 +297,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
/// Get items (A wrapper arround shows or collections) from a library. /// Get items (A wrapper arround shows or collections) from a library.
/// </summary> /// </summary>
@ -311,7 +311,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, object>> sort, Expression<Func<LibraryItem, object>> sort,
Pagination limit = default Pagination limit = default
) => GetItemsFromLibrary(id, where, new Sort<LibraryItem>(sort), limit); ) => GetItemsFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
/// <summary> /// <summary>
/// Get items (A wrapper arround shows or collections) from a library. /// Get items (A wrapper arround shows or collections) from a library.
/// </summary> /// </summary>
@ -324,7 +324,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
/// Get items (A wrapper arround shows or collections) from a library. /// Get items (A wrapper arround shows or collections) from a library.
/// </summary> /// </summary>
@ -338,8 +338,8 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, object>> sort, Expression<Func<LibraryItem, object>> sort,
Pagination limit = default Pagination limit = default
) => GetItemsFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit); ) => GetItemsFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a show. /// Get people's roles from a show.
/// </summary> /// </summary>
@ -349,10 +349,10 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID, Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
/// Get people's roles from a show. /// Get people's roles from a show.
/// </summary> /// </summary>
@ -366,7 +366,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetPeopleFromShow(showID, where, new Sort<PeopleRole>(sort), limit); ) => GetPeopleFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a show. /// Get people's roles from a show.
/// </summary> /// </summary>
@ -376,10 +376,10 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug, Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
/// Get people's roles from a show. /// Get people's roles from a show.
/// </summary> /// </summary>
@ -393,8 +393,8 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit); ) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a person. /// Get people's roles from a person.
/// </summary> /// </summary>
@ -404,10 +404,10 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetRolesFromPeople(int id, Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
/// Get people's roles from a person. /// Get people's roles from a person.
/// </summary> /// </summary>
@ -421,7 +421,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetRolesFromPeople(id, where, new Sort<PeopleRole>(sort), limit); ) => GetRolesFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a person. /// Get people's roles from a person.
/// </summary> /// </summary>
@ -431,10 +431,10 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug, Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
/// Get people's roles from a person. /// Get people's roles from a person.
/// </summary> /// </summary>
@ -449,7 +449,7 @@ namespace Kyoo.Abstractions.Controllers
Pagination limit = default Pagination limit = default
) => GetRolesFromPeople(slug, where, new Sort<PeopleRole>(sort), limit); ) => GetRolesFromPeople(slug, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Setup relations between a show, a library and a collection /// Setup relations between a show, a library and a collection
/// </summary> /// </summary>
@ -457,7 +457,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="libraryID">The library's ID to setup relations with (optional)</param> /// <param name="libraryID">The library's ID to setup relations with (optional)</param>
/// <param name="collectionID">The collection's ID to setup relations with (optional)</param> /// <param name="collectionID">The collection's ID to setup relations with (optional)</param>
Task AddShowLink(int showID, int? libraryID, int? collectionID); Task AddShowLink(int showID, int? libraryID, int? collectionID);
/// <summary> /// <summary>
/// Setup relations between a show, a library and a collection /// Setup relations between a show, a library and a collection
/// </summary> /// </summary>
@ -477,7 +477,7 @@ namespace Kyoo.Abstractions.Controllers
Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null, Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
Pagination limit = default) where T : class, IResource; Pagination limit = default) where T : class, IResource;
/// <summary> /// <summary>
/// Get all resources with filters /// Get all resources with filters
/// </summary> /// </summary>
@ -516,7 +516,7 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T">The type of resource</typeparam> /// <typeparam name="T">The type of resource</typeparam>
/// <returns>The resource registers and completed by database's informations (related items & so on)</returns> /// <returns>The resource registers and completed by database's informations (related items & so on)</returns>
Task<T> Create<T>([NotNull] T item) where T : class, IResource; Task<T> Create<T>([NotNull] T item) where T : class, IResource;
/// <summary> /// <summary>
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead. /// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
/// </summary> /// </summary>
@ -524,7 +524,7 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T">The type of resource</typeparam> /// <typeparam name="T">The type of resource</typeparam>
/// <returns>The newly created item or the existing value if it existed.</returns> /// <returns>The newly created item or the existing value if it existed.</returns>
Task<T> CreateIfNotExists<T>([NotNull] T item) where T : class, IResource; Task<T> CreateIfNotExists<T>([NotNull] T item) where T : class, IResource;
/// <summary> /// <summary>
/// Edit a resource /// Edit a resource
/// </summary> /// </summary>
@ -542,7 +542,7 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T">The type of resource to delete</typeparam> /// <typeparam name="T">The type of resource to delete</typeparam>
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete<T>(T item) where T : class, IResource; Task Delete<T>(T item) where T : class, IResource;
/// <summary> /// <summary>
/// Delete a resource by it's ID. /// Delete a resource by it's ID.
/// </summary> /// </summary>
@ -550,7 +550,7 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T">The type of resource to delete</typeparam> /// <typeparam name="T">The type of resource to delete</typeparam>
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete<T>(int id) where T : class, IResource; Task Delete<T>(int id) where T : class, IResource;
/// <summary> /// <summary>
/// Delete a resource by it's slug. /// Delete a resource by it's slug.
/// </summary> /// </summary>

View File

@ -20,17 +20,17 @@ namespace Kyoo.Abstractions.Controllers
/// A slug to identify this plugin in queries. /// A slug to identify this plugin in queries.
/// </summary> /// </summary>
string Slug { get; } string Slug { get; }
/// <summary> /// <summary>
/// The name of the plugin /// The name of the plugin
/// </summary> /// </summary>
string Name { get; } string Name { get; }
/// <summary> /// <summary>
/// The description of this plugin. This will be displayed on the "installed plugins" page. /// The description of this plugin. This will be displayed on the "installed plugins" page.
/// </summary> /// </summary>
string Description { get; } string Description { get; }
/// <summary> /// <summary>
/// <c>true</c> if the plugin should be enabled, <c>false</c> otherwise. /// <c>true</c> if the plugin should be enabled, <c>false</c> otherwise.
/// If a plugin is not enabled, no configure method will be called. /// If a plugin is not enabled, no configure method will be called.
@ -41,7 +41,7 @@ namespace Kyoo.Abstractions.Controllers
/// By default, a plugin is always enabled. This method can be overriden to change this behavior. /// By default, a plugin is always enabled. This method can be overriden to change this behavior.
/// </remarks> /// </remarks>
virtual bool Enabled => true; virtual bool Enabled => true;
/// <summary> /// <summary>
/// A list of types that will be available via the IOptions interfaces and will be listed inside /// A list of types that will be available via the IOptions interfaces and will be listed inside
/// an IConfiguration. /// an IConfiguration.
@ -64,7 +64,7 @@ namespace Kyoo.Abstractions.Controllers
/// <seealso cref="SA"/> /// <seealso cref="SA"/>
virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty; virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
/// <summary> /// <summary>
/// A configure method that will be run on plugin's startup. /// A configure method that will be run on plugin's startup.
/// </summary> /// </summary>
/// <param name="builder">The autofac service container to register services.</param> /// <param name="builder">The autofac service container to register services.</param>
@ -72,7 +72,7 @@ namespace Kyoo.Abstractions.Controllers
{ {
// Skipped // Skipped
} }
/// <summary> /// <summary>
/// A configure method that will be run on plugin's startup. /// A configure method that will be run on plugin's startup.
/// This is available for libraries that build upon a <see cref="IServiceCollection"/>, for more precise /// This is available for libraries that build upon a <see cref="IServiceCollection"/>, for more precise
@ -94,4 +94,4 @@ namespace Kyoo.Abstractions.Controllers
// Skipped // Skipped
} }
} }
} }

View File

@ -17,14 +17,14 @@ namespace Kyoo.Abstractions.Controllers
/// <exception cref="ItemNotFoundException">If no plugins match the query</exception> /// <exception cref="ItemNotFoundException">If no plugins match the query</exception>
/// <returns>A plugin that match the queries</returns> /// <returns>A plugin that match the queries</returns>
public T GetPlugin<T>(string name); public T GetPlugin<T>(string name);
/// <summary> /// <summary>
/// Get all plugins of the given type. /// Get all plugins of the given type.
/// </summary> /// </summary>
/// <typeparam name="T">The type of plugins to get</typeparam> /// <typeparam name="T">The type of plugins to get</typeparam>
/// <returns>A list of plugins matching the given type or an empty list of none match.</returns> /// <returns>A list of plugins matching the given type or an empty list of none match.</returns>
public ICollection<T> GetPlugins<T>(); public ICollection<T> GetPlugins<T>();
/// <summary> /// <summary>
/// Get all plugins currently running on Kyoo. This also includes deleted plugins if the app as not been restarted. /// Get all plugins currently running on Kyoo. This also includes deleted plugins if the app as not been restarted.
/// </summary> /// </summary>
@ -39,7 +39,7 @@ 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(ICollection<IPlugin> plugins); public void LoadPlugins(ICollection<IPlugin> plugins);
/// <summary> /// <summary>
/// Load plugins and their dependencies from the plugin directory. /// Load plugins and their dependencies from the plugin directory.
/// </summary> /// </summary>
@ -49,4 +49,4 @@ namespace Kyoo.Abstractions.Controllers
/// </param> /// </param>
public void LoadPlugins(params Type[] plugins); public void LoadPlugins(params Type[] plugins);
} }
} }

View File

@ -34,7 +34,7 @@ namespace Kyoo.Abstractions.Controllers
Count = count; Count = count;
AfterID = afterID; AfterID = afterID;
} }
/// <summary> /// <summary>
/// Implicitly create a new pagination from a limit number. /// Implicitly create a new pagination from a limit number.
/// </summary> /// </summary>
@ -57,7 +57,7 @@ namespace Kyoo.Abstractions.Controllers
/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order.
/// </summary> /// </summary>
public bool Descendant { get; } public bool Descendant { get; }
/// <summary> /// <summary>
/// Create a new <see cref="Sort{T}"/> instance. /// Create a new <see cref="Sort{T}"/> instance.
/// </summary> /// </summary>
@ -68,7 +68,7 @@ namespace Kyoo.Abstractions.Controllers
{ {
Key = key; Key = key;
Descendant = descendant; Descendant = descendant;
if (!Utility.IsPropertyExpression(Key)) if (!Utility.IsPropertyExpression(Key))
throw new ArgumentException("The given sort key is not valid."); throw new ArgumentException("The given sort key is not valid.");
} }
@ -86,7 +86,7 @@ namespace Kyoo.Abstractions.Controllers
Descendant = false; Descendant = false;
return; return;
} }
string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy; string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy;
string order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null; string order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null;
@ -116,7 +116,7 @@ namespace Kyoo.Abstractions.Controllers
/// </summary> /// </summary>
Type RepositoryType { get; } Type RepositoryType { get; }
} }
/// <summary> /// <summary>
/// A common repository for every resources. /// A common repository for every resources.
/// </summary> /// </summary>
@ -147,7 +147,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns> /// <returns>The resource found</returns>
[ItemNotNull] [ItemNotNull]
Task<T> Get(Expression<Func<T, bool>> where); Task<T> Get(Expression<Func<T, bool>> where);
/// <summary> /// <summary>
/// Get a resource from it's ID or null if it is not found. /// Get a resource from it's ID or null if it is not found.
/// </summary> /// </summary>
@ -169,7 +169,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns> /// <returns>The resource found</returns>
[ItemCanBeNull] [ItemCanBeNull]
Task<T> GetOrDefault(Expression<Func<T, bool>> where); Task<T> GetOrDefault(Expression<Func<T, bool>> where);
/// <summary> /// <summary>
/// Search for resources. /// Search for resources.
/// </summary> /// </summary>
@ -177,7 +177,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>A list of resources found</returns> /// <returns>A list of resources found</returns>
[ItemNotNull] [ItemNotNull]
Task<ICollection<T>> Search(string query); Task<ICollection<T>> Search(string query);
/// <summary> /// <summary>
/// Get every resources that match all filters /// Get every resources that match all filters
/// </summary> /// </summary>
@ -186,7 +186,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How pagination should be done (where to start and how many to return)</param> /// <param name="limit">How pagination should be done (where to start and how many to return)</param>
/// <returns>A list of resources that match every filters</returns> /// <returns>A list of resources that match every filters</returns>
[ItemNotNull] [ItemNotNull]
Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null, Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
@ -208,8 +208,8 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="where">A filter predicate</param> /// <param name="where">A filter predicate</param>
/// <returns>How many resources matched that filter</returns> /// <returns>How many resources matched that filter</returns>
Task<int> GetCount(Expression<Func<T, bool>> where = null); Task<int> GetCount(Expression<Func<T, bool>> where = null);
/// <summary> /// <summary>
/// Create a new resource. /// Create a new resource.
/// </summary> /// </summary>
@ -217,7 +217,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource registers and completed by database's information (related items & so on)</returns> /// <returns>The resource registers and completed by database's information (related items & so on)</returns>
[ItemNotNull] [ItemNotNull]
Task<T> Create([NotNull] T obj); Task<T> Create([NotNull] T obj);
/// <summary> /// <summary>
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead. /// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
/// </summary> /// </summary>
@ -225,7 +225,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The newly created item or the existing value if it existed.</returns> /// <returns>The newly created item or the existing value if it existed.</returns>
[ItemNotNull] [ItemNotNull]
Task<T> CreateIfNotExists([NotNull] T obj); Task<T> CreateIfNotExists([NotNull] T obj);
/// <summary> /// <summary>
/// Edit a resource /// Edit a resource
/// </summary> /// </summary>
@ -235,7 +235,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource edited and completed by database's information (related items & so on)</returns> /// <returns>The resource edited and completed by database's information (related items & so on)</returns>
[ItemNotNull] [ItemNotNull]
Task<T> Edit([NotNull] T edited, bool resetOld); Task<T> Edit([NotNull] T edited, bool resetOld);
/// <summary> /// <summary>
/// Delete a resource by it's ID /// Delete a resource by it's ID
/// </summary> /// </summary>
@ -254,7 +254,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="obj">The resource to delete</param> /// <param name="obj">The resource to delete</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete([NotNull] T obj); Task Delete([NotNull] T obj);
/// <summary> /// <summary>
/// Delete all resources that match the predicate. /// Delete all resources that match the predicate.
/// </summary> /// </summary>
@ -299,7 +299,7 @@ namespace Kyoo.Abstractions.Controllers
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns> /// <returns>The season found</returns>
Task<Season> Get(int showID, int seasonNumber); Task<Season> Get(int showID, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's show slug and it's seasonNumber /// Get a season from it's show slug and it's seasonNumber
/// </summary> /// </summary>
@ -308,7 +308,7 @@ namespace Kyoo.Abstractions.Controllers
/// <exception cref="ItemNotFoundException">If the item is not found</exception> /// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns> /// <returns>The season found</returns>
Task<Season> Get(string showSlug, int seasonNumber); Task<Season> Get(string showSlug, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's showID and it's seasonNumber or null if it is not found. /// Get a season from it's showID and it's seasonNumber or null if it is not found.
/// </summary> /// </summary>
@ -316,7 +316,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="seasonNumber">The season's number</param> /// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns> /// <returns>The season found</returns>
Task<Season> GetOrDefault(int showID, int seasonNumber); Task<Season> GetOrDefault(int showID, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's show slug and it's seasonNumber or null if it is not found. /// Get a season from it's show slug and it's seasonNumber or null if it is not found.
/// </summary> /// </summary>
@ -325,7 +325,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The season found</returns> /// <returns>The season found</returns>
Task<Season> GetOrDefault(string showSlug, int seasonNumber); Task<Season> GetOrDefault(string showSlug, int seasonNumber);
} }
/// <summary> /// <summary>
/// The repository to handle episodes /// The repository to handle episodes
/// </summary> /// </summary>
@ -366,7 +366,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="episodeNumber">The episode's number</param> /// <param name="episodeNumber">The episode's number</param>
/// <returns>The episode found</returns> /// <returns>The episode found</returns>
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber); Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
/// <summary> /// <summary>
/// Get a episode from it's showID and it's absolute number. /// Get a episode from it's showID and it's absolute number.
/// </summary> /// </summary>
@ -389,7 +389,7 @@ namespace Kyoo.Abstractions.Controllers
/// A repository to handle tracks /// A repository to handle tracks
/// </summary> /// </summary>
public interface ITrackRepository : IRepository<Track> { } public interface ITrackRepository : IRepository<Track> { }
/// <summary> /// <summary>
/// A repository to handle libraries. /// A repository to handle libraries.
/// </summary> /// </summary>
@ -425,7 +425,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, object>> sort, Expression<Func<LibraryItem, object>> sort,
Pagination limit = default Pagination limit = default
) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit); ) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
/// <summary> /// <summary>
/// Get items (A wrapper around shows or collections) from a library. /// Get items (A wrapper around shows or collections) from a library.
/// </summary> /// </summary>
@ -451,18 +451,18 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, object>> sort, Expression<Func<LibraryItem, object>> sort,
Pagination limit = default Pagination limit = default
) => GetFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit); ) => GetFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
} }
/// <summary> /// <summary>
/// A repository for collections /// A repository for collections
/// </summary> /// </summary>
public interface ICollectionRepository : IRepository<Collection> { } public interface ICollectionRepository : IRepository<Collection> { }
/// <summary> /// <summary>
/// A repository for genres. /// A repository for genres.
/// </summary> /// </summary>
public interface IGenreRepository : IRepository<Genre> { } public interface IGenreRepository : IRepository<Genre> { }
/// <summary> /// <summary>
/// A repository for studios. /// A repository for studios.
/// </summary> /// </summary>
@ -482,7 +482,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromShow(int showID, Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
@ -498,7 +498,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetFromShow(showID, where, new Sort<PeopleRole>(sort), limit); ) => GetFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a show. /// Get people's roles from a show.
/// </summary> /// </summary>
@ -508,7 +508,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromShow(string showSlug, Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
@ -524,7 +524,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit); ) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a person. /// Get people's roles from a person.
/// </summary> /// </summary>
@ -534,7 +534,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromPeople(int id, Task<ICollection<PeopleRole>> GetFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
@ -550,7 +550,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort, Expression<Func<PeopleRole, object>> sort,
Pagination limit = default Pagination limit = default
) => GetFromPeople(id, where, new Sort<PeopleRole>(sort), limit); ) => GetFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
/// <summary> /// <summary>
/// Get people's roles from a person. /// Get people's roles from a person.
/// </summary> /// </summary>
@ -560,7 +560,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param> /// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns> /// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromPeople(string slug, Task<ICollection<PeopleRole>> GetFromPeople(string slug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default); Pagination limit = default);
/// <summary> /// <summary>
@ -591,7 +591,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">Pagination information (where to start and how many to get)</param> /// <param name="limit">Pagination information (where to start and how many to get)</param>
/// <typeparam name="T">The type of metadata to retrieve</typeparam> /// <typeparam name="T">The type of metadata to retrieve</typeparam>
/// <returns>A filtered list of external ids.</returns> /// <returns>A filtered list of external ids.</returns>
Task<ICollection<MetadataID>> GetMetadataID<T>(Expression<Func<MetadataID, bool>> where = null, Task<ICollection<MetadataID>> GetMetadataID<T>(Expression<Func<MetadataID, bool>> where = null,
Sort<MetadataID> sort = default, Sort<MetadataID> sort = default,
Pagination limit = default) Pagination limit = default)
where T : class, IMetadata; where T : class, IMetadata;
@ -609,9 +609,9 @@ namespace Kyoo.Abstractions.Controllers
) where T : class, IMetadata ) where T : class, IMetadata
=> GetMetadataID<T>(where, new Sort<MetadataID>(sort), limit); => GetMetadataID<T>(where, new Sort<MetadataID>(sort), limit);
} }
/// <summary> /// <summary>
/// A repository to handle users. /// A repository to handle users.
/// </summary> /// </summary>
public interface IUserRepository : IRepository<User> {} public interface IUserRepository : IRepository<User> { }
} }

View File

@ -19,22 +19,22 @@ namespace Kyoo.Abstractions.Controllers
/// The name of this parameter. /// The name of this parameter.
/// </summary> /// </summary>
public string Name { get; init; } public string Name { get; init; }
/// <summary> /// <summary>
/// The description of this parameter. /// The description of this parameter.
/// </summary> /// </summary>
public string Description { get; init; } public string Description { get; init; }
/// <summary> /// <summary>
/// The type of this parameter. /// The type of this parameter.
/// </summary> /// </summary>
public Type Type { get; init; } public Type Type { get; init; }
/// <summary> /// <summary>
/// Is this parameter required or can it be ignored? /// Is this parameter required or can it be ignored?
/// </summary> /// </summary>
public bool IsRequired { get; init; } public bool IsRequired { get; init; }
/// <summary> /// <summary>
/// The default value of this object. /// The default value of this object.
/// </summary> /// </summary>
@ -44,7 +44,7 @@ namespace Kyoo.Abstractions.Controllers
/// The value of the parameter. /// The value of the parameter.
/// </summary> /// </summary>
private object Value { get; init; } private object Value { get; init; }
/// <summary> /// <summary>
/// Create a new task parameter. /// Create a new task parameter.
/// </summary> /// </summary>
@ -61,7 +61,7 @@ namespace Kyoo.Abstractions.Controllers
Type = typeof(T) Type = typeof(T)
}; };
} }
/// <summary> /// <summary>
/// Create a new required task parameter. /// Create a new required task parameter.
/// </summary> /// </summary>
@ -79,7 +79,7 @@ namespace Kyoo.Abstractions.Controllers
IsRequired = true IsRequired = true
}; };
} }
/// <summary> /// <summary>
/// Create a parameter's value to give to a task. /// Create a parameter's value to give to a task.
/// </summary> /// </summary>
@ -104,9 +104,9 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>A new parameter's value for this current parameter</returns> /// <returns>A new parameter's value for this current parameter</returns>
public TaskParameter CreateValue(object value) public TaskParameter CreateValue(object value)
{ {
return this with {Value = value}; return this with { Value = value };
} }
/// <summary> /// <summary>
/// Get the value of this parameter. If the value is of the wrong type, it will be converted. /// Get the value of this parameter. If the value is of the wrong type, it will be converted.
/// </summary> /// </summary>
@ -140,12 +140,12 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="name">The name of the task (case sensitive)</param> /// <param name="name">The name of the task (case sensitive)</param>
public TaskParameter this[string name] => this.FirstOrDefault(x => x.Name == name); public TaskParameter this[string name] => this.FirstOrDefault(x => x.Name == name);
/// <summary> /// <summary>
/// Create a new, empty, <see cref="TaskParameters"/> /// Create a new, empty, <see cref="TaskParameters"/>
/// </summary> /// </summary>
public TaskParameters() {} public TaskParameters() { }
/// <summary> /// <summary>
/// Create a <see cref="TaskParameters"/> with an initial parameters content /// Create a <see cref="TaskParameters"/> with an initial parameters content
/// </summary> /// </summary>
@ -155,7 +155,7 @@ namespace Kyoo.Abstractions.Controllers
AddRange(parameters); AddRange(parameters);
} }
} }
/// <summary> /// <summary>
/// A common interface that tasks should implement. /// A common interface that tasks should implement.
/// </summary> /// </summary>
@ -168,7 +168,7 @@ namespace Kyoo.Abstractions.Controllers
/// All parameters that this task as. Every one of them will be given to the run function with a value. /// All parameters that this task as. Every one of them will be given to the run function with a value.
/// </returns> /// </returns>
public TaskParameters GetParameters(); public TaskParameters GetParameters();
/// <summary> /// <summary>
/// Start this task. /// Start this task.
/// </summary> /// </summary>
@ -191,4 +191,4 @@ namespace Kyoo.Abstractions.Controllers
[NotNull] IProgress<float> progress, [NotNull] IProgress<float> progress,
CancellationToken cancellationToken); CancellationToken cancellationToken);
} }
} }

View File

@ -35,7 +35,7 @@ namespace Kyoo.Abstractions.Controllers
/// <exception cref="ItemNotFoundException"> /// <exception cref="ItemNotFoundException">
/// The task could not be found. /// The task could not be found.
/// </exception> /// </exception>
void StartTask(string taskSlug, void StartTask(string taskSlug,
[NotNull] IProgress<float> progress, [NotNull] IProgress<float> progress,
Dictionary<string, object> arguments = null, Dictionary<string, object> arguments = null,
CancellationToken? cancellationToken = null); CancellationToken? cancellationToken = null);
@ -66,13 +66,13 @@ namespace Kyoo.Abstractions.Controllers
Dictionary<string, object> arguments = null, Dictionary<string, object> arguments = null,
CancellationToken? cancellationToken = null) CancellationToken? cancellationToken = null)
where T : ITask; where T : ITask;
/// <summary> /// <summary>
/// Get all currently running tasks /// Get all currently running tasks
/// </summary> /// </summary>
/// <returns>A list of currently running tasks.</returns> /// <returns>A list of currently running tasks.</returns>
ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks(); ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks();
/// <summary> /// <summary>
/// Get all available tasks /// Get all available tasks
/// </summary> /// </summary>

View File

@ -1,6 +1,6 @@
using Kyoo.Abstractions.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Kyoo.Abstractions.Models;
namespace Kyoo.Abstractions.Controllers namespace Kyoo.Abstractions.Controllers
{ {

View File

@ -23,9 +23,9 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="action">The action to run</param> /// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param> /// <param name="priority">The priority of the new action</param>
/// <returns>A new <see cref="StartupAction"/></returns> /// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction New(Action action, int priority) public static StartupAction New(Action action, int priority)
=> new(action, priority); => new(action, priority);
/// <summary> /// <summary>
/// Create a new <see cref="StartupAction"/>. /// Create a new <see cref="StartupAction"/>.
/// </summary> /// </summary>
@ -33,9 +33,9 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="priority">The priority of the new action</param> /// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam> /// <typeparam name="T">A 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> New<T>(Action<T> action, int priority) public static StartupAction<T> New<T>(Action<T> action, int priority)
=> new(action, priority); => new(action, priority);
/// <summary> /// <summary>
/// Create a new <see cref="StartupAction"/>. /// Create a new <see cref="StartupAction"/>.
/// </summary> /// </summary>
@ -44,9 +44,9 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T">A dependency that this action will use.</typeparam> /// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam> /// <typeparam name="T2">A second 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> New<T, T2>(Action<T, T2> action, int priority) public static StartupAction<T, T2> New<T, T2>(Action<T, T2> action, int priority)
=> new(action, priority); => new(action, priority);
/// <summary> /// <summary>
/// Create a new <see cref="StartupAction"/>. /// Create a new <see cref="StartupAction"/>.
/// </summary> /// </summary>
@ -56,11 +56,11 @@ 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>(Action<T, T2, T3> action, int priority) public static StartupAction<T, T2, T3> New<T, T2, T3>(Action<T, T2, T3> action, int priority)
=> new(action, priority); => new(action, priority);
} }
/// <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>
@ -81,7 +81,7 @@ namespace Kyoo.Abstractions.Controllers
/// <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);
} }
/// <summary> /// <summary>
/// A <see cref="IStartupAction"/> with no dependencies. /// A <see cref="IStartupAction"/> with no dependencies.
/// </summary> /// </summary>
@ -94,7 +94,7 @@ namespace Kyoo.Abstractions.Controllers
/// <inheritdoc /> /// <inheritdoc />
public int Priority { get; } public int Priority { get; }
/// <summary> /// <summary>
/// Create a new <see cref="StartupAction"/>. /// Create a new <see cref="StartupAction"/>.
/// </summary> /// </summary>
@ -126,7 +126,7 @@ namespace Kyoo.Abstractions.Controllers
/// <inheritdoc /> /// <inheritdoc />
public int Priority { get; } public int Priority { get; }
/// <summary> /// <summary>
/// Create a new <see cref="StartupAction{T}"/>. /// Create a new <see cref="StartupAction{T}"/>.
/// </summary> /// </summary>
@ -144,7 +144,7 @@ namespace Kyoo.Abstractions.Controllers
_action.Invoke(provider.GetRequiredService<T>()); _action.Invoke(provider.GetRequiredService<T>());
} }
} }
/// <summary> /// <summary>
/// A <see cref="IStartupAction"/> with two dependencies. /// A <see cref="IStartupAction"/> with two dependencies.
/// </summary> /// </summary>
@ -180,7 +180,7 @@ namespace Kyoo.Abstractions.Controllers
); );
} }
} }
/// <summary> /// <summary>
/// A <see cref="IStartupAction"/> with three dependencies. /// A <see cref="IStartupAction"/> with three dependencies.
/// </summary> /// </summary>
@ -196,7 +196,7 @@ namespace Kyoo.Abstractions.Controllers
/// <inheritdoc /> /// <inheritdoc />
public int Priority { get; } public int Priority { get; }
/// <summary> /// <summary>
/// Create a new <see cref="StartupAction{T, T2, T3}"/>. /// Create a new <see cref="StartupAction{T, T2, T3}"/>.
/// </summary> /// </summary>

View File

@ -20,13 +20,13 @@ namespace Kyoo.Abstractions.Models.Attributes
/// If multiples files with the same schemes exists, an exception will be thrown. /// If multiples files with the same schemes exists, an exception will be thrown.
/// </remarks> /// </remarks>
public string[] Scheme { get; } public string[] Scheme { get; }
/// <summary> /// <summary>
/// <c>true</c> if the scheme should be removed from the path before calling /// <c>true</c> if the scheme should be removed from the path before calling
/// methods of this <see cref="IFileSystem"/>, <c>false</c> otherwise. /// methods of this <see cref="IFileSystem"/>, <c>false</c> otherwise.
/// </summary> /// </summary>
public bool StripScheme { get; set; } public bool StripScheme { get; set; }
/// <summary> /// <summary>
/// Create a new <see cref="FileSystemMetadataAttribute"/> using the specified schemes. /// Create a new <see cref="FileSystemMetadataAttribute"/> using the specified schemes.
@ -36,7 +36,7 @@ namespace Kyoo.Abstractions.Models.Attributes
{ {
Scheme = schemes; Scheme = schemes;
} }
/// <summary> /// <summary>
/// Create a new <see cref="FileSystemMetadataAttribute"/> using a dictionary of metadata. /// Create a new <see cref="FileSystemMetadataAttribute"/> using a dictionary of metadata.
/// </summary> /// </summary>

View File

@ -23,7 +23,7 @@ namespace Kyoo.Abstractions.Models.Permissions
Overall, Overall,
Admin Admin
} }
/// <summary> /// <summary>
/// Specify permissions needed for the API. /// Specify permissions needed for the API.
/// </summary> /// </summary>
@ -63,7 +63,7 @@ namespace Kyoo.Abstractions.Models.Permissions
Kind = permission; Kind = permission;
Group = group; Group = group;
} }
/// <inheritdoc /> /// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{ {
@ -97,7 +97,7 @@ namespace Kyoo.Abstractions.Models.Permissions
/// The needed permission kind. /// The needed permission kind.
/// </summary> /// </summary>
public Kind Kind { get; } public Kind Kind { get; }
/// <summary> /// <summary>
/// Ask a permission to run an action. /// Ask a permission to run an action.
/// </summary> /// </summary>
@ -118,7 +118,7 @@ namespace Kyoo.Abstractions.Models.Permissions
type = type[..^3]; type = type[..^3];
Type = type.ToLower(); Type = type.ToLower();
} }
/// <summary> /// <summary>
/// Ask a permission to run an action. /// Ask a permission to run an action.
/// </summary> /// </summary>
@ -134,7 +134,7 @@ namespace Kyoo.Abstractions.Models.Permissions
{ {
Kind = permission; Kind = permission;
} }
/// <inheritdoc /> /// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{ {
@ -158,7 +158,7 @@ namespace Kyoo.Abstractions.Models.Permissions
/// <param name="attribute">The permission attribute to validate</param> /// <param name="attribute">The permission attribute to validate</param>
/// <returns>An authorization filter used to validate the permission</returns> /// <returns>An authorization filter used to validate the permission</returns>
IFilterMetadata Create(PermissionAttribute attribute); IFilterMetadata Create(PermissionAttribute attribute);
/// <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.

View File

@ -19,11 +19,11 @@ namespace Kyoo.Abstractions.Models.Attributes
/// The name of the field containing the related resource's ID. /// The name of the field containing the related resource's ID.
/// </summary> /// </summary>
public string RelationID { get; } public string RelationID { get; }
/// <summary> /// <summary>
/// Create a new <see cref="LoadableRelationAttribute"/>. /// Create a new <see cref="LoadableRelationAttribute"/>.
/// </summary> /// </summary>
public LoadableRelationAttribute() {} public LoadableRelationAttribute() { }
/// <summary> /// <summary>
/// Create a new <see cref="LoadableRelationAttribute"/> with a baking relationID field. /// Create a new <see cref="LoadableRelationAttribute"/> with a baking relationID field.

View File

@ -6,13 +6,13 @@ namespace Kyoo.Abstractions.Models.Attributes
/// Remove an property from the serialization pipeline. It will simply be skipped. /// Remove an property from the serialization pipeline. It will simply be skipped.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class SerializeIgnoreAttribute : Attribute {} public class SerializeIgnoreAttribute : Attribute { }
/// <summary> /// <summary>
/// Remove a property from the deserialization pipeline. The user can't input value for this property. /// Remove a property from the deserialization pipeline. The user can't input value for this property.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class DeserializeIgnoreAttribute : Attribute {} public class DeserializeIgnoreAttribute : Attribute { }
/// <summary> /// <summary>
/// Change the way the field is serialized. It allow one to use a string format like formatting instead of the default value. /// Change the way the field is serialized. It allow one to use a string format like formatting instead of the default value.
@ -25,7 +25,7 @@ namespace Kyoo.Abstractions.Models.Attributes
/// The format string to use. /// The format string to use.
/// </summary> /// </summary>
public string Format { get; } public string Format { get; }
/// <summary> /// <summary>
/// Create a new <see cref="SerializeAsAttribute"/> with the selected format. /// Create a new <see cref="SerializeAsAttribute"/> with the selected format.
/// </summary> /// </summary>

View File

@ -16,12 +16,12 @@ namespace Kyoo.Abstractions.Models.Attributes
/// The slug of the task, used to start it. /// The slug of the task, used to start it.
/// </summary> /// </summary>
public string Slug { get; } public string Slug { get; }
/// <summary> /// <summary>
/// The name of the task that will be displayed to the user. /// The name of the task that will be displayed to the user.
/// </summary> /// </summary>
public string Name { get; } public string Name { get; }
/// <summary> /// <summary>
/// A quick description of what this task will do. /// A quick description of what this task will do.
/// </summary> /// </summary>
@ -31,18 +31,18 @@ namespace Kyoo.Abstractions.Models.Attributes
/// Should this task be automatically run at app startup? /// Should this task be automatically run at app startup?
/// </summary> /// </summary>
public bool RunOnStartup { get; set; } public bool RunOnStartup { get; set; }
/// <summary> /// <summary>
/// The priority of this task. Only used if <see cref="RunOnStartup"/> is true. /// The priority of this task. Only used if <see cref="RunOnStartup"/> is true.
/// It allow one to specify witch task will be started first as tasked are run on a Priority's descending order. /// It allow one to specify witch task will be started first as tasked are run on a Priority's descending order.
/// </summary> /// </summary>
public int Priority { get; set; } public int Priority { get; set; }
/// <summary> /// <summary>
/// <c>true</c> if this task should not be displayed to the user, <c>false</c> otherwise. /// <c>true</c> if this task should not be displayed to the user, <c>false</c> otherwise.
/// </summary> /// </summary>
public bool IsHidden { get; set; } public bool IsHidden { get; set; }
/// <summary> /// <summary>
/// Create a new <see cref="TaskMetadataAttribute"/> with the given slug, name and description. /// Create a new <see cref="TaskMetadataAttribute"/> with the given slug, name and description.
@ -56,7 +56,7 @@ namespace Kyoo.Abstractions.Models.Attributes
Name = name; Name = name;
Description = description; Description = description;
} }
/// <summary> /// <summary>
/// Create a new <see cref="TaskMetadataAttribute"/> using a dictionary of metadata. /// Create a new <see cref="TaskMetadataAttribute"/> using a dictionary of metadata.
/// </summary> /// </summary>

View File

@ -9,16 +9,16 @@ namespace Kyoo.Abstractions.Models
/// The start time of the chapter (in second from the start of the episode). /// The start time of the chapter (in second from the start of the episode).
/// </summary> /// </summary>
public float StartTime { get; set; } public float StartTime { get; set; }
/// <summary> /// <summary>
/// The end time of the chapter (in second from the start of the episode)&. /// The end time of the chapter (in second from the start of the episode).
/// </summary> /// </summary>
public float EndTime { get; set; } public float EndTime { get; set; }
/// <summary> /// <summary>
/// The name of this chapter. This should be a human-readable name that could be presented to the user. /// The name of this chapter. This should be a human-readable name that could be presented to the user.
/// There should be well-known chapters name for commonly used chapters. /// There should be well-known chapters name for commonly used chapters.
/// For example, use "Opening" for the introduction-song and "Credits" for the end chapter with credits. /// For example, use "Opening" for the introduction-song and "Credits" for the end chapter with credits.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
@ -35,4 +35,4 @@ namespace Kyoo.Abstractions.Models
Name = name; Name = name;
} }
} }
} }

View File

@ -21,7 +21,7 @@ namespace Kyoo.Abstractions.Models
/// </summary> /// </summary>
public Type Type { get; } public Type Type { get; }
/// <summary> /// <summary>
/// Create a new <see cref="ConfigurationReference"/> using a given path and type. /// Create a new <see cref="ConfigurationReference"/> using a given path and type.
/// This method does not create sub configuration resources. Please see <see cref="CreateReference"/> /// This method does not create sub configuration resources. Please see <see cref="CreateReference"/>
@ -75,7 +75,7 @@ namespace Kyoo.Abstractions.Models
return ret; return ret;
} }
/// <summary> /// <summary>
/// Return the list of configuration reference a type has. /// Return the list of configuration reference a type has.
/// </summary> /// </summary>

View File

@ -15,7 +15,7 @@ namespace Kyoo.Abstractions.Models.Exceptions
public DuplicatedItemException() public DuplicatedItemException()
: base("Already exists in the database.") : base("Already exists in the database.")
{ } { }
/// <summary> /// <summary>
/// Create a new <see cref="DuplicatedItemException"/> with a custom message. /// Create a new <see cref="DuplicatedItemException"/> with a custom message.
/// </summary> /// </summary>
@ -23,7 +23,7 @@ namespace Kyoo.Abstractions.Models.Exceptions
public DuplicatedItemException(string message) public DuplicatedItemException(string message)
: base(message) : base(message)
{ } { }
/// <summary> /// <summary>
/// The serialization constructor /// The serialization constructor
/// </summary> /// </summary>

View File

@ -15,24 +15,24 @@ namespace Kyoo.Abstractions.Models.Exceptions
/// </summary> /// </summary>
public TaskFailedException() public TaskFailedException()
: base("A task failed.") : base("A task failed.")
{} { }
/// <summary> /// <summary>
/// Create a new <see cref="TaskFailedException"/> with a custom message. /// Create a new <see cref="TaskFailedException"/> with a custom message.
/// </summary> /// </summary>
/// <param name="message">The message to use.</param> /// <param name="message">The message to use.</param>
public TaskFailedException(string message) public TaskFailedException(string message)
: base(message) : base(message)
{} { }
/// <summary> /// <summary>
/// Create a new <see cref="TaskFailedException"/> wrapping another exception. /// Create a new <see cref="TaskFailedException"/> wrapping another exception.
/// </summary> /// </summary>
/// <param name="exception">The exception to wrap.</param> /// <param name="exception">The exception to wrap.</param>
public TaskFailedException(Exception exception) public TaskFailedException(Exception exception)
: base(exception) : base(exception)
{} { }
/// <summary> /// <summary>
/// The serialization constructor /// The serialization constructor
/// </summary> /// </summary>

View File

@ -14,7 +14,7 @@ namespace Kyoo.Abstractions.Models
Movie, Movie,
Collection Collection
} }
/// <summary> /// <summary>
/// A type union between <see cref="Show"/> and <see cref="Collection"/>. /// A type union between <see cref="Show"/> and <see cref="Collection"/>.
/// This is used to list content put inside a library. /// This is used to list content put inside a library.
@ -23,30 +23,30 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The title of the show or collection. /// The title of the show or collection.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// The summary of the show or collection. /// The summary of the show or collection.
/// </summary> /// </summary>
public string Overview { get; set; } public string Overview { get; set; }
/// <summary> /// <summary>
/// Is this show airing, not aired yet or finished? This is only applicable for shows. /// Is this show airing, not aired yet or finished? This is only applicable for shows.
/// </summary> /// </summary>
public Status? Status { get; set; } public Status? Status { get; set; }
/// <summary> /// <summary>
/// The date this show or collection started airing. It can be null if this is unknown. /// The date this show or collection started airing. It can be null if this is unknown.
/// </summary> /// </summary>
public DateTime? StartAir { get; set; } public DateTime? StartAir { get; set; }
/// <summary> /// <summary>
/// The date this show or collection finished airing. /// The date this show or collection finished airing.
/// It must be after the <see cref="StartAir"/> but can be the same (example: for movies). /// It must be after the <see cref="StartAir"/> but can be the same (example: for movies).
@ -64,17 +64,17 @@ namespace Kyoo.Abstractions.Models
/// </summary> /// </summary>
[SerializeAs("{HOST}/api/{Type:l}/{Slug}/poster")] [SerializeAs("{HOST}/api/{Type:l}/{Slug}/poster")]
public string Poster => Images?.GetValueOrDefault(Models.Images.Poster); public string Poster => Images?.GetValueOrDefault(Models.Images.Poster);
/// <summary> /// <summary>
/// The type of this item (ether a collection, a show or a movie). /// The type of this item (ether a collection, a show or a movie).
/// </summary> /// </summary>
public ItemType Type { get; set; } public ItemType Type { get; set; }
/// <summary> /// <summary>
/// Create a new, empty <see cref="LibraryItem"/>. /// Create a new, empty <see cref="LibraryItem"/>.
/// </summary> /// </summary>
public LibraryItem() {} public LibraryItem() { }
/// <summary> /// <summary>
/// Create a <see cref="LibraryItem"/> from a show. /// Create a <see cref="LibraryItem"/> from a show.
@ -92,7 +92,7 @@ namespace Kyoo.Abstractions.Models
Images = show.Images; Images = show.Images;
Type = show.IsMovie ? ItemType.Movie : ItemType.Show; Type = show.IsMovie ? ItemType.Movie : ItemType.Show;
} }
/// <summary> /// <summary>
/// Create a <see cref="LibraryItem"/> from a collection /// Create a <see cref="LibraryItem"/> from a collection
/// </summary> /// </summary>
@ -125,7 +125,7 @@ namespace Kyoo.Abstractions.Models
Images = x.Images, Images = x.Images,
Type = x.IsMovie ? ItemType.Movie : ItemType.Show Type = x.IsMovie ? ItemType.Movie : ItemType.Show
}; };
/// <summary> /// <summary>
/// An expression to create a <see cref="LibraryItem"/> representing a collection. /// An expression to create a <see cref="LibraryItem"/> representing a collection.
/// </summary> /// </summary>

View File

@ -15,12 +15,12 @@ namespace Kyoo.Abstractions.Models
/// The link of the current page. /// The link of the current page.
/// </summary> /// </summary>
public Uri This { get; } public Uri This { get; }
/// <summary> /// <summary>
/// The link of the first page. /// The link of the first page.
/// </summary> /// </summary>
public Uri First { get; } public Uri First { get; }
/// <summary> /// <summary>
/// The link of the next page. /// The link of the next page.
/// </summary> /// </summary>
@ -30,13 +30,13 @@ namespace Kyoo.Abstractions.Models
/// The number of items in the current page. /// The number of items in the current page.
/// </summary> /// </summary>
public int Count => Items.Count; public int Count => Items.Count;
/// <summary> /// <summary>
/// The list of items in the page. /// The list of items in the page.
/// </summary> /// </summary>
public ICollection<T> Items { get; } public ICollection<T> Items { get; }
/// <summary> /// <summary>
/// Create a new <see cref="Page{T}"/>. /// Create a new <see cref="Page{T}"/>.
/// </summary> /// </summary>
@ -72,7 +72,7 @@ namespace Kyoo.Abstractions.Models
query["afterID"] = items.Last().ID.ToString(); query["afterID"] = items.Last().ID.ToString();
Next = new Uri(url + query.ToQueryString()); Next = new Uri(url + query.ToQueryString());
} }
query.Remove("afterID"); query.Remove("afterID");
First = new Uri(url + query.ToQueryString()); First = new Uri(url + query.ToQueryString());
} }

View File

@ -12,10 +12,10 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug => ForPeople ? Show.Slug : People.Slug; public string Slug => ForPeople ? Show.Slug : People.Slug;
/// <summary> /// <summary>
/// Should this role be used as a Show substitute (the value is <c>true</c>) or /// Should this role be used as a Show substitute (the value is <c>true</c>) or
/// as a People substitute (the value is <c>false</c>). /// as a People substitute (the value is <c>false</c>).
@ -30,7 +30,7 @@ namespace Kyoo.Abstractions.Models
/// The people that played this role. /// The people that played this role.
/// </summary> /// </summary>
public People People { get; set; } public People People { get; set; }
/// <summary> /// <summary>
/// The ID of the Show where the People playing in. /// The ID of the Show where the People playing in.
/// </summary> /// </summary>
@ -39,13 +39,13 @@ namespace Kyoo.Abstractions.Models
/// The show where the People played in. /// The show where the People played in.
/// </summary> /// </summary>
public Show Show { get; set; } public Show Show { get; set; }
/// <summary> /// <summary>
/// The type of work the person has done for the show. /// The type of work the person has done for the show.
/// That can be something like "Actor", "Writer", "Music", "Voice Actor"... /// That can be something like "Actor", "Writer", "Music", "Voice Actor"...
/// </summary> /// </summary>
public string Type { get; set; } public string Type { get; set; }
/// <summary> /// <summary>
/// The role the People played. /// The role the People played.
/// This is mostly used to inform witch character was played for actor and voice actors. /// This is mostly used to inform witch character was played for actor and voice actors.

View File

@ -12,10 +12,10 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The name of this collection. /// The name of this collection.
/// </summary> /// </summary>
@ -23,7 +23,7 @@ namespace Kyoo.Abstractions.Models
/// <inheritdoc /> /// <inheritdoc />
public Dictionary<int, string> Images { get; set; } public Dictionary<int, string> Images { get; set; }
/// <summary> /// <summary>
/// The path of this poster. /// The path of this poster.
/// By default, the http path for this poster is returned from the public API. /// By default, the http path for this poster is returned from the public API.
@ -37,17 +37,17 @@ namespace Kyoo.Abstractions.Models
/// The description of this collection. /// The description of this collection.
/// </summary> /// </summary>
public string Overview { get; set; } public string Overview { get; set; }
/// <summary> /// <summary>
/// The list of shows contained in this collection. /// The list of shows contained in this collection.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Show> Shows { get; set; } [LoadableRelation] public ICollection<Show> Shows { get; set; }
/// <summary> /// <summary>
/// The list of libraries that contains this collection. /// The list of libraries that contains this collection.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Library> Libraries { get; set; } [LoadableRelation] public ICollection<Library> Libraries { get; set; }
/// <inheritdoc /> /// <inheritdoc />
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; } [EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -22,15 +22,15 @@ namespace Kyoo.Abstractions.Models
{ {
if (ShowSlug != null || Show != null) if (ShowSlug != null || Show != null)
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber); return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
return ShowID != 0 return ShowID != 0
? GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber) ? GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber)
: null; : null;
} }
[UsedImplicitly] [NotNull] private set [UsedImplicitly] [NotNull] private set
{ {
if (value == null) if (value == null)
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)e(?<episode>\d+)"); Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)e(?<episode>\d+)");
if (match.Success) if (match.Success)
@ -59,7 +59,7 @@ namespace Kyoo.Abstractions.Models
/// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed. /// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed.
/// </summary> /// </summary>
[SerializeIgnore] public string ShowSlug { private get; set; } [SerializeIgnore] public string ShowSlug { private get; set; }
/// <summary> /// <summary>
/// The ID of the Show containing this episode. /// The ID of the Show containing this episode.
/// </summary> /// </summary>
@ -68,7 +68,7 @@ namespace Kyoo.Abstractions.Models
/// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>. /// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
/// </summary> /// </summary>
[LoadableRelation(nameof(ShowID))] public Show Show { get; set; } [LoadableRelation(nameof(ShowID))] public Show Show { get; set; }
/// <summary> /// <summary>
/// The ID of the Season containing this episode. /// The ID of the Season containing this episode.
/// </summary> /// </summary>
@ -87,22 +87,22 @@ namespace Kyoo.Abstractions.Models
/// The season in witch this episode is in. /// The season in witch this episode is in.
/// </summary> /// </summary>
public int? SeasonNumber { get; set; } public int? SeasonNumber { get; set; }
/// <summary> /// <summary>
/// The number of this episode in it's season. /// The number of this episode in it's season.
/// </summary> /// </summary>
public int? EpisodeNumber { get; set; } public int? EpisodeNumber { get; set; }
/// <summary> /// <summary>
/// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season. /// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season.
/// </summary> /// </summary>
public int? AbsoluteNumber { get; set; } public int? AbsoluteNumber { get; set; }
/// <summary> /// <summary>
/// The path of the video file for this episode. Any format supported by a <see cref="IFileSystem"/> is allowed. /// The path of the video file for this episode. Any format supported by a <see cref="IFileSystem"/> is allowed.
/// </summary> /// </summary>
[SerializeIgnore] public string Path { get; set; } [SerializeIgnore] public string Path { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public Dictionary<int, string> Images { get; set; } public Dictionary<int, string> Images { get; set; }
@ -114,17 +114,17 @@ namespace Kyoo.Abstractions.Models
[SerializeAs("{HOST}/api/episodes/{Slug}/thumbnail")] [SerializeAs("{HOST}/api/episodes/{Slug}/thumbnail")]
[Obsolete("Use Images instead of this, this is only kept for the API response.")] [Obsolete("Use Images instead of this, this is only kept for the API response.")]
public string Thumb => Images?.GetValueOrDefault(Models.Images.Thumbnail); public string Thumb => Images?.GetValueOrDefault(Models.Images.Thumbnail);
/// <summary> /// <summary>
/// The title of this episode. /// The title of this episode.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// The overview of this episode. /// The overview of this episode.
/// </summary> /// </summary>
public string Overview { get; set; } public string Overview { get; set; }
/// <summary> /// <summary>
/// The release date of this episode. It can be null if unknown. /// The release date of this episode. It can be null if unknown.
/// </summary> /// </summary>
@ -137,7 +137,7 @@ namespace Kyoo.Abstractions.Models
/// The list of tracks this episode has. This lists video, audio and subtitles available. /// The list of tracks this episode has. This lists video, audio and subtitles available.
/// </summary> /// </summary>
[EditableRelation] [LoadableRelation] public ICollection<Track> Tracks { get; set; } [EditableRelation] [LoadableRelation] public ICollection<Track> Tracks { get; set; }
/// <summary> /// <summary>
/// Get the slug of an episode. /// Get the slug of an episode.
@ -157,8 +157,8 @@ namespace Kyoo.Abstractions.Models
/// </param> /// </param>
/// <returns>The slug corresponding to the given arguments</returns> /// <returns>The slug corresponding to the given arguments</returns>
/// <exception cref="ArgumentNullException">The given show slug was null.</exception> /// <exception cref="ArgumentNullException">The given show slug was null.</exception>
public static string GetSlug([NotNull] string showSlug, public static string GetSlug([NotNull] string showSlug,
int? seasonNumber, int? seasonNumber,
int? episodeNumber, int? episodeNumber,
int? absoluteNumber = null) int? absoluteNumber = null)
{ {

View File

@ -11,15 +11,15 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The name of this genre. /// The name of this genre.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The list of shows that have this genre. /// The list of shows that have this genre.
/// </summary> /// </summary>
@ -28,8 +28,8 @@ namespace Kyoo.Abstractions.Models
/// <summary> /// <summary>
/// Create a new, empty <see cref="Genre"/>. /// Create a new, empty <see cref="Genre"/>.
/// </summary> /// </summary>
public Genre() {} public Genre() { }
/// <summary> /// <summary>
/// Create a new <see cref="Genre"/> and specify it's <see cref="Name"/>. /// Create a new <see cref="Genre"/> and specify it's <see cref="Name"/>.
/// The <see cref="Slug"/> is automatically calculated from it's name. /// The <see cref="Slug"/> is automatically calculated from it's name.

View File

@ -14,7 +14,8 @@ namespace Kyoo.Abstractions.Models
/// <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>
[EditableRelation] [LoadableRelation] [EditableRelation]
[LoadableRelation]
public ICollection<MetadataID> ExternalIDs { get; set; } public ICollection<MetadataID> ExternalIDs { get; set; }
} }

View File

@ -15,7 +15,7 @@ namespace Kyoo.Abstractions.Models
/// this field is automatically assigned by the <see cref="IRepository{T}"/>. /// this field is automatically assigned by the <see cref="IRepository{T}"/>.
/// </remarks> /// </remarks>
public int ID { get; set; } public int ID { get; set; }
/// <summary> /// <summary>
/// A human-readable identifier that can be used instead of an ID. /// A human-readable identifier that can be used instead of an ID.
/// A slug must be unique for a type of resource but it can be changed. /// A slug must be unique for a type of resource but it can be changed.
@ -24,6 +24,6 @@ namespace Kyoo.Abstractions.Models
/// There is no setter for a slug since it can be computed from other fields. /// There is no setter for a slug since it can be computed from other fields.
/// For example, a season slug is {ShowSlug}-s{SeasonNumber}. /// For example, a season slug is {ShowSlug}-s{SeasonNumber}.
/// </remarks> /// </remarks>
public string Slug { get; } public string Slug { get; }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Kyoo.Abstractions.Models
/// An arbitrary index should not be used, instead use indexes from <see cref="Models.Images"/> /// An arbitrary index should not be used, instead use indexes from <see cref="Models.Images"/>
/// </remarks> /// </remarks>
public Dictionary<int, string> Images { get; set; } public Dictionary<int, string> Images { get; set; }
// TODO remove Posters properties add them via the json serializer for every IThumbnails // TODO remove Posters properties add them via the json serializer for every IThumbnails
} }

View File

@ -10,15 +10,15 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The name of this library. /// The name of this library.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The list of paths that this library is responsible for. This is mainly used by the Scan task. /// The list of paths that this library is responsible for. This is mainly used by the Scan task.
/// </summary> /// </summary>
@ -33,7 +33,7 @@ namespace Kyoo.Abstractions.Models
/// The list of shows in this library. /// The list of shows in this library.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Show> Shows { get; set; } [LoadableRelation] public ICollection<Show> Shows { get; set; }
/// <summary> /// <summary>
/// The list of collections in this library. /// The list of collections in this library.
/// </summary> /// </summary>

View File

@ -11,15 +11,15 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The name of this person. /// The name of this person.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public Dictionary<int, string> Images { get; set; } public Dictionary<int, string> Images { get; set; }
@ -31,10 +31,10 @@ namespace Kyoo.Abstractions.Models
[SerializeAs("{HOST}/api/people/{Slug}/poster")] [SerializeAs("{HOST}/api/people/{Slug}/poster")]
[Obsolete("Use Images instead of this, this is only kept for the API response.")] [Obsolete("Use Images instead of this, this is only kept for the API response.")]
public string Poster => Images?.GetValueOrDefault(Models.Images.Poster); public string Poster => Images?.GetValueOrDefault(Models.Images.Poster);
/// <inheritdoc /> /// <inheritdoc />
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; } [EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
/// <summary> /// <summary>
/// The list of roles this person has played in. See <see cref="PeopleRole"/> for more information. /// The list of roles this person has played in. See <see cref="PeopleRole"/> for more information.
/// </summary> /// </summary>

View File

@ -14,15 +14,15 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The name of this provider. /// The name of this provider.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public Dictionary<int, string> Images { get; set; } public Dictionary<int, string> Images { get; set; }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -13,7 +13,7 @@ namespace Kyoo.Abstractions.Models
public class Season : IResource, IMetadata, IThumbnails public class Season : IResource, IMetadata, IThumbnails
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
[Computed] public string Slug [Computed] public string Slug
@ -27,7 +27,7 @@ namespace Kyoo.Abstractions.Models
[UsedImplicitly] [NotNull] private set [UsedImplicitly] [NotNull] private set
{ {
Match match = Regex.Match(value ?? "", @"(?<show>.+)-s(?<season>\d+)"); Match match = Regex.Match(value ?? "", @"(?<show>.+)-s(?<season>\d+)");
if (!match.Success) if (!match.Success)
throw new ArgumentException("Invalid season slug. Format: {showSlug}-s{seasonNumber}"); throw new ArgumentException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
ShowSlug = match.Groups["show"].Value; ShowSlug = match.Groups["show"].Value;
@ -39,7 +39,7 @@ namespace Kyoo.Abstractions.Models
/// The slug of the Show that contain this episode. If this is not set, this season is ill-formed. /// The slug of the Show that contain this episode. If this is not set, this season is ill-formed.
/// </summary> /// </summary>
[SerializeIgnore] public string ShowSlug { private get; set; } [SerializeIgnore] public string ShowSlug { private get; set; }
/// <summary> /// <summary>
/// The ID of the Show containing this season. /// The ID of the Show containing this season.
/// </summary> /// </summary>
@ -59,17 +59,17 @@ namespace Kyoo.Abstractions.Models
/// The title of this season. /// The title of this season.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// A quick overview of this season. /// A quick overview of this season.
/// </summary> /// </summary>
public string Overview { get; set; } public string Overview { get; set; }
/// <summary> /// <summary>
/// The starting air date of this season. /// The starting air date of this season.
/// </summary> /// </summary>
public DateTime? StartDate { get; set; } public DateTime? StartDate { get; set; }
/// <summary> /// <summary>
/// The ending date of this season. /// The ending date of this season.
/// </summary> /// </summary>
@ -86,7 +86,7 @@ namespace Kyoo.Abstractions.Models
[SerializeAs("{HOST}/api/seasons/{Slug}/thumb")] [SerializeAs("{HOST}/api/seasons/{Slug}/thumb")]
[Obsolete("Use Images instead of this, this is only kept for the API response.")] [Obsolete("Use Images instead of this, this is only kept for the API response.")]
public string Poster => Images?.GetValueOrDefault(Models.Images.Poster); public string Poster => Images?.GetValueOrDefault(Models.Images.Poster);
/// <inheritdoc /> /// <inheritdoc />
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; } [EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }

View File

@ -12,31 +12,31 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The title of this show. /// The title of this show.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// The list of alternative titles of this show. /// The list of alternative titles of this show.
/// </summary> /// </summary>
[EditableRelation] public string[] Aliases { get; set; } [EditableRelation] public string[] Aliases { get; set; }
/// <summary> /// <summary>
/// The path of the root directory of this show. /// The path of the root directory of this show.
/// This can be any kind of path supported by <see cref="IFileSystem"/> /// This can be any kind of path supported by <see cref="IFileSystem"/>
/// </summary> /// </summary>
[SerializeIgnore] public string Path { get; set; } [SerializeIgnore] public string Path { get; set; }
/// <summary> /// <summary>
/// The summary of this show. /// The summary of this show.
/// </summary> /// </summary>
public string Overview { get; set; } public string Overview { get; set; }
/// <summary> /// <summary>
/// Is this show airing, not aired yet or finished? /// Is this show airing, not aired yet or finished?
/// </summary> /// </summary>
@ -48,12 +48,12 @@ namespace Kyoo.Abstractions.Models
/// TODO for now, this is set to a youtube url. It should be cached and converted to a local file. /// TODO for now, this is set to a youtube url. It should be cached and converted to a local file.
[Obsolete("Use Images instead of this, this is only kept for the API response.")] [Obsolete("Use Images instead of this, this is only kept for the API response.")]
public string TrailerUrl => Images?.GetValueOrDefault(Models.Images.Trailer); public string TrailerUrl => Images?.GetValueOrDefault(Models.Images.Trailer);
/// <summary> /// <summary>
/// The date this show started airing. It can be null if this is unknown. /// The date this show started airing. It can be null if this is unknown.
/// </summary> /// </summary>
public DateTime? StartAir { get; set; } public DateTime? StartAir { get; set; }
/// <summary> /// <summary>
/// The date this show finished airing. /// The date this show finished airing.
/// It must be after the <see cref="StartAir"/> but can be the same (example: for movies). /// It must be after the <see cref="StartAir"/> but can be the same (example: for movies).
@ -108,39 +108,39 @@ namespace Kyoo.Abstractions.Models
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>. /// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
/// </summary> /// </summary>
[LoadableRelation(nameof(StudioID))] [EditableRelation] public Studio Studio { get; set; } [LoadableRelation(nameof(StudioID))] [EditableRelation] public Studio Studio { get; set; }
/// <summary> /// <summary>
/// The list of genres (themes) this show has. /// The list of genres (themes) this show has.
/// </summary> /// </summary>
[LoadableRelation] [EditableRelation] public ICollection<Genre> Genres { get; set; } [LoadableRelation] [EditableRelation] public ICollection<Genre> Genres { get; set; }
/// <summary> /// <summary>
/// The list of people that made this show. /// The list of people that made this show.
/// </summary> /// </summary>
[LoadableRelation] [EditableRelation] public ICollection<PeopleRole> People { get; set; } [LoadableRelation] [EditableRelation] public ICollection<PeopleRole> People { get; set; }
/// <summary> /// <summary>
/// The different seasons in this show. If this is a movie, this list is always null or empty. /// The different seasons in this show. If this is a movie, this list is always null or empty.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Season> Seasons { get; set; } [LoadableRelation] public ICollection<Season> Seasons { get; set; }
/// <summary> /// <summary>
/// The list of episodes in this show. /// The list of episodes in this show.
/// If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to null). /// If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to null).
/// Having an episode is necessary to store metadata and tracks. /// Having an episode is necessary to store metadata and tracks.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Episode> Episodes { get; set; } [LoadableRelation] public ICollection<Episode> Episodes { get; set; }
/// <summary> /// <summary>
/// The list of libraries that contains this show. /// The list of libraries that contains this show.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Library> Libraries { get; set; } [LoadableRelation] public ICollection<Library> Libraries { get; set; }
/// <summary> /// <summary>
/// The list of collections that contains this show. /// The list of collections that contains this show.
/// </summary> /// </summary>
[LoadableRelation] public ICollection<Collection> Collections { get; set; } [LoadableRelation] public ICollection<Collection> Collections { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public void OnMerge(object merged) public void OnMerge(object merged)
{ {

View File

@ -11,15 +11,15 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// The name of this studio. /// The name of this studio.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// The list of shows that are made by this studio. /// The list of shows that are made by this studio.
/// </summary> /// </summary>
@ -27,7 +27,7 @@ namespace Kyoo.Abstractions.Models
/// <inheritdoc /> /// <inheritdoc />
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; } [EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
/// <summary> /// <summary>
/// Create a new, empty, <see cref="Studio"/>. /// Create a new, empty, <see cref="Studio"/>.
/// </summary> /// </summary>

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -27,7 +27,7 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
[Computed] public string Slug [Computed] public string Slug
{ {
@ -42,12 +42,14 @@ namespace Kyoo.Abstractions.Models
{ {
if (value == null) if (value == null)
throw new ArgumentNullException(nameof(value)); throw new ArgumentNullException(nameof(value));
Match match = Regex.Match(value, Match match = Regex.Match(value,
@"(?<ep>[^\.]+)\.(?<lang>\w{0,3})(-(?<index>\d+))?(\.(?<forced>forced))?\.(?<type>\w+)(\.\w*)?"); @"(?<ep>[^\.]+)\.(?<lang>\w{0,3})(-(?<index>\d+))?(\.(?<forced>forced))?\.(?<type>\w+)(\.\w*)?");
if (!match.Success) if (!match.Success)
{
throw new ArgumentException("Invalid track slug. " + throw new ArgumentException("Invalid track slug. " +
"Format: {episodeSlug}.{language}[-{index}][.forced].{type}[.{extension}]"); "Format: {episodeSlug}.{language}[-{index}][.forced].{type}[.{extension}]");
}
EpisodeSlug = match.Groups["ep"].Value; EpisodeSlug = match.Groups["ep"].Value;
Language = match.Groups["lang"].Value; Language = match.Groups["lang"].Value;
@ -58,53 +60,53 @@ namespace Kyoo.Abstractions.Models
Type = Enum.Parse<StreamType>(match.Groups["type"].Value, true); Type = Enum.Parse<StreamType>(match.Groups["type"].Value, true);
} }
} }
/// <summary> /// <summary>
/// The slug of the episode that contain this track. If this is not set, this track is ill-formed. /// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
/// </summary> /// </summary>
[SerializeIgnore] public string EpisodeSlug { private get; set; } [SerializeIgnore] public string EpisodeSlug { private get; set; }
/// <summary> /// <summary>
/// The title of the stream. /// The title of the stream.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// The language of this stream (as a ISO-639-2 language code) /// The language of this stream (as a ISO-639-2 language code)
/// </summary> /// </summary>
public string Language { get; set; } public string Language { get; set; }
/// <summary> /// <summary>
/// The codec of this stream. /// The codec of this stream.
/// </summary> /// </summary>
public string Codec { get; set; } public string Codec { get; set; }
/// <summary> /// <summary>
/// Is this stream the default one of it's type? /// Is this stream the default one of it's type?
/// </summary> /// </summary>
public bool IsDefault { get; set; } public bool IsDefault { get; set; }
/// <summary> /// <summary>
/// Is this stream tagged as forced? /// Is this stream tagged as forced?
/// </summary> /// </summary>
public bool IsForced { get; set; } public bool IsForced { get; set; }
/// <summary> /// <summary>
/// Is this track extern to the episode's file? /// Is this track extern to the episode's file?
/// </summary> /// </summary>
public bool IsExternal { get; set; } public bool IsExternal { get; set; }
/// <summary> /// <summary>
/// The path of this track. /// The path of this track.
/// </summary> /// </summary>
[SerializeIgnore] public string Path { get; set; } [SerializeIgnore] public string Path { get; set; }
/// <summary> /// <summary>
/// The type of this stream. /// The type of this stream.
/// </summary> /// </summary>
[SerializeIgnore] public StreamType Type { get; set; } [SerializeIgnore] public StreamType Type { get; set; }
/// <summary> /// <summary>
/// The ID of the episode that uses this track. /// The ID of the episode that uses this track.
/// </summary> /// </summary>
@ -137,7 +139,7 @@ namespace Kyoo.Abstractions.Models
name += " Forced"; name += " Forced";
if (IsExternal) if (IsExternal)
name += " (External)"; name += " (External)";
if (Title is {Length: > 1}) if (Title is { Length: > 1 })
name += " - " + Title; name += " - " + Title;
return name; return name;
} }
@ -164,8 +166,8 @@ namespace Kyoo.Abstractions.Models
public static string BuildSlug(string baseSlug, public static string BuildSlug(string baseSlug,
StreamType type) StreamType type)
{ {
return baseSlug.EndsWith($".{type}", StringComparison.InvariantCultureIgnoreCase) return baseSlug.EndsWith($".{type}", StringComparison.InvariantCultureIgnoreCase)
? baseSlug ? baseSlug
: $"{baseSlug}.{type.ToString().ToLowerInvariant()}"; : $"{baseSlug}.{type.ToString().ToLowerInvariant()}";
} }
} }

View File

@ -9,30 +9,30 @@ namespace Kyoo.Abstractions.Models
{ {
/// <inheritdoc /> /// <inheritdoc />
public int ID { get; set; } public int ID { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string Slug { get; set; } public string Slug { get; set; }
/// <summary> /// <summary>
/// A username displayed to the user. /// A username displayed to the user.
/// </summary> /// </summary>
public string Username { get; set; } public string Username { get; set; }
/// <summary> /// <summary>
/// The user email address. /// The user email address.
/// </summary> /// </summary>
public string Email { get; set; } public string Email { get; set; }
/// <summary> /// <summary>
/// The user password (hashed, it can't be read like that). The hashing format is implementation defined. /// The user password (hashed, it can't be read like that). The hashing format is implementation defined.
/// </summary> /// </summary>
public string Password { get; set; } public string Password { get; set; }
/// <summary> /// <summary>
/// The list of permissions of the user. The format of this is implementation dependent. /// The list of permissions of the user. The format of this is implementation dependent.
/// </summary> /// </summary>
public string[] Permissions { get; set; } public string[] Permissions { get; set; }
/// <summary> /// <summary>
/// Arbitrary extra data that can be used by specific authentication implementations. /// Arbitrary extra data that can be used by specific authentication implementations.
/// </summary> /// </summary>
@ -45,13 +45,13 @@ namespace Kyoo.Abstractions.Models
/// The list of shows the user has finished. /// The list of shows the user has finished.
/// </summary> /// </summary>
public ICollection<Show> Watched { get; set; } public ICollection<Show> Watched { get; set; }
/// <summary> /// <summary>
/// The list of episodes the user is watching (stopped in progress or the next episode of the show) /// The list of episodes the user is watching (stopped in progress or the next episode of the show)
/// </summary> /// </summary>
public ICollection<WatchedEpisode> CurrentlyWatching { get; set; } public ICollection<WatchedEpisode> CurrentlyWatching { get; set; }
} }
/// <summary> /// <summary>
/// Metadata of episode currently watching by an user /// Metadata of episode currently watching by an user
/// </summary> /// </summary>
@ -61,17 +61,17 @@ namespace Kyoo.Abstractions.Models
/// The ID of the user that started watching this episode. /// The ID of the user that started watching this episode.
/// </summary> /// </summary>
public int UserID { get; set; } public int UserID { get; set; }
/// <summary> /// <summary>
/// The ID of the episode started. /// The ID of the episode started.
/// </summary> /// </summary>
public int EpisodeID { get; set; } public int EpisodeID { get; set; }
/// <summary> /// <summary>
/// The <see cref="Episode"/> started. /// The <see cref="Episode"/> started.
/// </summary> /// </summary>
public Episode Episode { get; set; } public Episode Episode { get; set; }
/// <summary> /// <summary>
/// Where the player has stopped watching the episode (between 0 and 100). /// Where the player has stopped watching the episode (between 0 and 100).
/// </summary> /// </summary>

View File

@ -11,32 +11,32 @@ namespace Kyoo.Abstractions.Models
/// The query of the search request. /// The query of the search request.
/// </summary> /// </summary>
public string Query { get; init; } public string Query { get; init; }
/// <summary> /// <summary>
/// The collections that matched the search. /// The collections that matched the search.
/// </summary> /// </summary>
public ICollection<Collection> Collections { get; init; } public ICollection<Collection> Collections { get; init; }
/// <summary> /// <summary>
/// The shows that matched the search. /// The shows that matched the search.
/// </summary> /// </summary>
public ICollection<Show> Shows { get; init; } public ICollection<Show> Shows { get; init; }
/// <summary> /// <summary>
/// The episodes that matched the search. /// The episodes that matched the search.
/// </summary> /// </summary>
public ICollection<Episode> Episodes { get; init; } public ICollection<Episode> Episodes { get; init; }
/// <summary> /// <summary>
/// The people that matched the search. /// The people that matched the search.
/// </summary> /// </summary>
public ICollection<People> People { get; init; } public ICollection<People> People { get; init; }
/// <summary> /// <summary>
/// The genres that matched the search. /// The genres that matched the search.
/// </summary> /// </summary>
public ICollection<Genre> Genres { get; init; } public ICollection<Genre> Genres { get; init; }
/// <summary> /// <summary>
/// The studios that matched the search. /// The studios that matched the search.
/// </summary> /// </summary>

View File

@ -20,7 +20,7 @@ namespace Kyoo.Abstractions.Models
/// The ID of the episode associated with this item. /// The ID of the episode associated with this item.
/// </summary> /// </summary>
public int EpisodeID { get; set; } public int EpisodeID { get; set; }
/// <summary> /// <summary>
/// The slug of this episode. /// The slug of this episode.
/// </summary> /// </summary>
@ -30,54 +30,54 @@ namespace Kyoo.Abstractions.Models
/// The title of the show containing this episode. /// The title of the show containing this episode.
/// </summary> /// </summary>
public string ShowTitle { get; set; } public string ShowTitle { get; set; }
/// <summary> /// <summary>
/// The slug of the show containing this episode /// The slug of the show containing this episode
/// </summary> /// </summary>
public string ShowSlug { get; set; } public string ShowSlug { get; set; }
/// <summary> /// <summary>
/// The season in witch this episode is in. /// The season in witch this episode is in.
/// </summary> /// </summary>
public int? SeasonNumber { get; set; } public int? SeasonNumber { get; set; }
/// <summary> /// <summary>
/// The number of this episode is it's season. /// The number of this episode is it's season.
/// </summary> /// </summary>
public int? EpisodeNumber { get; set; } public int? EpisodeNumber { get; set; }
/// <summary> /// <summary>
/// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season. /// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season.
/// </summary> /// </summary>
public int? AbsoluteNumber { get; set; } public int? AbsoluteNumber { get; set; }
/// <summary> /// <summary>
/// The title of this episode. /// The title of this episode.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// The release date of this episode. It can be null if unknown. /// The release date of this episode. It can be null if unknown.
/// </summary> /// </summary>
public DateTime? ReleaseDate { get; set; } public DateTime? ReleaseDate { get; set; }
/// <summary> /// <summary>
/// The path of the video file for this episode. Any format supported by a <see cref="IFileSystem"/> is allowed. /// The path of the video file for this episode. Any format supported by a <see cref="IFileSystem"/> is allowed.
/// </summary> /// </summary>
[SerializeIgnore] public string Path { get; set; } [SerializeIgnore] public string Path { get; set; }
/// <summary> /// <summary>
/// The episode that come before this one if you follow usual watch orders. /// The episode that come before this one if you follow usual watch orders.
/// If this is the first episode or this is a movie, it will be null. /// If this is the first episode or this is a movie, it will be null.
/// </summary> /// </summary>
public Episode PreviousEpisode { get; set; } public Episode PreviousEpisode { get; set; }
/// <summary> /// <summary>
/// The episode that come after this one if you follow usual watch orders. /// The episode that come after this one if you follow usual watch orders.
/// If this is the last aired episode or this is a movie, it will be null. /// If this is the last aired episode or this is a movie, it will be null.
/// </summary> /// </summary>
public Episode NextEpisode { get; set; } public Episode NextEpisode { get; set; }
/// <summary> /// <summary>
/// <c>true</c> if this is a movie, <c>false</c> otherwise. /// <c>true</c> if this is a movie, <c>false</c> otherwise.
/// </summary> /// </summary>
@ -89,14 +89,14 @@ namespace Kyoo.Abstractions.Models
/// This can be disabled using the internal query flag. /// This can be disabled using the internal query flag.
/// </summary> /// </summary>
[SerializeAs("{HOST}/api/show/{ShowSlug}/poster")] public string Poster { get; set; } [SerializeAs("{HOST}/api/show/{ShowSlug}/poster")] public string Poster { get; set; }
/// <summary> /// <summary>
/// The path of this item's logo. /// The path of this item's logo.
/// By default, the http path for the logo is returned from the public API. /// By default, the http path for the logo is returned from the public API.
/// This can be disabled using the internal query flag. /// This can be disabled using the internal query flag.
/// </summary> /// </summary>
[SerializeAs("{HOST}/api/show/{ShowSlug}/logo")] public string Logo { get; set; } [SerializeAs("{HOST}/api/show/{ShowSlug}/logo")] public string Logo { get; set; }
/// <summary> /// <summary>
/// The path of this item's backdrop. /// The path of this item's backdrop.
/// By default, the http path for the backdrop is returned from the public API. /// By default, the http path for the backdrop is returned from the public API.
@ -109,27 +109,27 @@ namespace Kyoo.Abstractions.Models
/// Common containers are mp4, mkv, avi and so on. /// Common containers are mp4, mkv, avi and so on.
/// </summary> /// </summary>
public string Container { get; set; } public string Container { get; set; }
/// <summary> /// <summary>
/// The video track. See <see cref="Track"/> for more information. /// The video track. See <see cref="Track"/> for more information.
/// </summary> /// </summary>
public Track Video { get; set; } public Track Video { get; set; }
/// <summary> /// <summary>
/// The list of audio tracks. See <see cref="Track"/> for more information. /// The list of audio tracks. See <see cref="Track"/> for more information.
/// </summary> /// </summary>
public ICollection<Track> Audios { get; set; } public ICollection<Track> Audios { get; set; }
/// <summary> /// <summary>
/// The list of subtitles tracks. See <see cref="Track"/> for more information. /// The list of subtitles tracks. See <see cref="Track"/> for more information.
/// </summary> /// </summary>
public ICollection<Track> Subtitles { get; set; } public ICollection<Track> Subtitles { get; set; }
/// <summary> /// <summary>
/// The list of chapters. See <see cref="Chapter"/> for more information. /// The list of chapters. See <see cref="Chapter"/> for more information.
/// </summary> /// </summary>
public ICollection<Chapter> Chapters { get; set; } public ICollection<Chapter> Chapters { get; set; }
/// <summary> /// <summary>
/// Create a <see cref="WatchItem"/> from an <see cref="Episode"/>. /// Create a <see cref="WatchItem"/> from an <see cref="Episode"/>.
@ -146,15 +146,15 @@ namespace Kyoo.Abstractions.Models
await library.Load(ep, x => x.Show); await library.Load(ep, x => x.Show);
await library.Load(ep, x => x.Tracks); await library.Load(ep, x => x.Tracks);
if (!ep.Show.IsMovie && ep.SeasonNumber != null && ep.EpisodeNumber != null) if (!ep.Show.IsMovie && ep.SeasonNumber != null && ep.EpisodeNumber != null)
{ {
if (ep.EpisodeNumber > 1) if (ep.EpisodeNumber > 1)
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value, ep.EpisodeNumber.Value - 1); previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value, ep.EpisodeNumber.Value - 1);
else if (ep.SeasonNumber > 1) else if (ep.SeasonNumber > 1)
{ {
previous = (await library.GetAll(x => x.ShowID == ep.ShowID previous = (await library.GetAll(x => x.ShowID == ep.ShowID
&& x.SeasonNumber == ep.SeasonNumber.Value - 1, && x.SeasonNumber == ep.SeasonNumber.Value - 1,
limit: 1, limit: 1,
sort: new Sort<Episode>(x => x.EpisodeNumber, true)) sort: new Sort<Episode>(x => x.EpisodeNumber, true))
).FirstOrDefault(); ).FirstOrDefault();
@ -167,12 +167,12 @@ namespace Kyoo.Abstractions.Models
} }
else if (!ep.Show.IsMovie && ep.AbsoluteNumber != null) else if (!ep.Show.IsMovie && ep.AbsoluteNumber != null)
{ {
previous = await library.GetOrDefault<Episode>(x => x.ShowID == ep.ShowID previous = await library.GetOrDefault<Episode>(x => x.ShowID == ep.ShowID
&& x.AbsoluteNumber == ep.EpisodeNumber + 1); && x.AbsoluteNumber == ep.EpisodeNumber + 1);
next = await library.GetOrDefault<Episode>(x => x.ShowID == ep.ShowID next = await library.GetOrDefault<Episode>(x => x.ShowID == ep.ShowID
&& x.AbsoluteNumber == ep.AbsoluteNumber + 1); && x.AbsoluteNumber == ep.AbsoluteNumber + 1);
} }
return new WatchItem return new WatchItem
{ {
EpisodeID = ep.ID, EpisodeID = ep.ID,
@ -188,7 +188,7 @@ namespace Kyoo.Abstractions.Models
Container = PathIO.GetExtension(ep.Path)![1..], Container = PathIO.GetExtension(ep.Path)![1..],
Video = ep.Tracks.FirstOrDefault(x => x.Type == StreamType.Video), Video = ep.Tracks.FirstOrDefault(x => x.Type == StreamType.Video),
Audios = ep.Tracks.Where(x => x.Type == StreamType.Audio).ToArray(), Audios = ep.Tracks.Where(x => x.Type == StreamType.Audio).ToArray(),
Subtitles = ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray(), Subtitles = ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray(),
PreviousEpisode = previous, PreviousEpisode = previous,
NextEpisode = next, NextEpisode = next,
Chapters = await GetChapters(ep.Path) Chapters = await GetChapters(ep.Path)
@ -200,7 +200,7 @@ namespace Kyoo.Abstractions.Models
private static async Task<ICollection<Chapter>> GetChapters(string episodePath) private static async Task<ICollection<Chapter>> GetChapters(string episodePath)
{ {
string path = PathIO.Combine( string path = PathIO.Combine(
PathIO.GetDirectoryName(episodePath)!, PathIO.GetDirectoryName(episodePath)!,
"Chapters", "Chapters",
PathIO.GetFileNameWithoutExtension(episodePath) + ".txt" PathIO.GetFileNameWithoutExtension(episodePath) + ".txt"
); );

View File

@ -30,7 +30,7 @@ namespace Kyoo.Abstractions
/// <param name="builder">The container</param> /// <param name="builder">The container</param>
/// <typeparam name="T">The type of the task</typeparam> /// <typeparam name="T">The type of the task</typeparam>
/// <returns>The registration builder of this new provider. That can be used to edit the registration.</returns> /// <returns>The registration builder of this new provider. That can be used to edit the registration.</returns>
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle> public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
RegisterProvider<T>(this ContainerBuilder builder) RegisterProvider<T>(this ContainerBuilder builder)
where T : class, IMetadataProvider where T : class, IMetadataProvider
{ {
@ -46,7 +46,7 @@ namespace Kyoo.Abstractions
/// If your repository implements a special interface, please use <see cref="RegisterRepository{T,T2}"/> /// If your repository implements a special interface, please use <see cref="RegisterRepository{T,T2}"/>
/// </remarks> /// </remarks>
/// <returns>The initial container.</returns> /// <returns>The initial container.</returns>
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle> public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
RegisterRepository<T>(this ContainerBuilder builder) RegisterRepository<T>(this ContainerBuilder builder)
where T : IBaseRepository where T : IBaseRepository
{ {

View File

@ -42,7 +42,7 @@ namespace Kyoo.Utils
} }
return Generator(self, mapper); return Generator(self, mapper);
} }
/// <summary> /// <summary>
/// A map where the mapping function is asynchronous. /// A map where the mapping function is asynchronous.
/// Note: <see cref="SelectAsync{T,T2}"/> might interest you. /// Note: <see cref="SelectAsync{T,T2}"/> might interest you.
@ -54,7 +54,7 @@ namespace Kyoo.Utils
/// <returns>The list mapped as an AsyncEnumerable</returns> /// <returns>The list mapped as an AsyncEnumerable</returns>
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception> /// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
[LinqTunnel] [LinqTunnel]
public static IAsyncEnumerable<T2> MapAsync<T, T2>([NotNull] this IEnumerable<T> self, public static IAsyncEnumerable<T2> MapAsync<T, T2>([NotNull] this IEnumerable<T> self,
[NotNull] Func<T, int, Task<T2>> mapper) [NotNull] Func<T, int, Task<T2>> mapper)
{ {
if (self == null) if (self == null)
@ -76,7 +76,7 @@ namespace Kyoo.Utils
return Generator(self, mapper); return Generator(self, mapper);
} }
/// <summary> /// <summary>
/// An asynchronous version of Select. /// An asynchronous version of Select.
/// </summary> /// </summary>
@ -87,7 +87,7 @@ namespace Kyoo.Utils
/// <returns>The list mapped as an AsyncEnumerable</returns> /// <returns>The list mapped as an AsyncEnumerable</returns>
/// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception> /// <exception cref="ArgumentNullException">The list or the mapper can't be null</exception>
[LinqTunnel] [LinqTunnel]
public static IAsyncEnumerable<T2> SelectAsync<T, T2>([NotNull] this IEnumerable<T> self, public static IAsyncEnumerable<T2> SelectAsync<T, T2>([NotNull] this IEnumerable<T> self,
[NotNull] Func<T, Task<T2>> mapper) [NotNull] Func<T, Task<T2>> mapper)
{ {
if (self == null) if (self == null)
@ -159,7 +159,7 @@ namespace Kyoo.Utils
do do
{ {
yield return enumerator.Current; yield return enumerator.Current;
} }
while (enumerator.MoveNext()); while (enumerator.MoveNext());
} }
@ -179,7 +179,7 @@ namespace Kyoo.Utils
foreach (T i in self) foreach (T i in self)
action(i); action(i);
} }
/// <summary> /// <summary>
/// A foreach used as a function with a little specificity: the list can be null. /// A foreach used as a function with a little specificity: the list can be null.
/// </summary> /// </summary>
@ -192,7 +192,7 @@ namespace Kyoo.Utils
foreach (object i in self) foreach (object i in self)
action(i); action(i);
} }
/// <summary> /// <summary>
/// A foreach used as a function with a little specificity: the list can be null. /// A foreach used as a function with a little specificity: the list can be null.
/// </summary> /// </summary>
@ -205,7 +205,7 @@ namespace Kyoo.Utils
foreach (object i in self) foreach (object i in self)
await action(i); await action(i);
} }
/// <summary> /// <summary>
/// A foreach used as a function with a little specificity: the list can be null. /// A foreach used as a function with a little specificity: the list can be null.
/// </summary> /// </summary>
@ -219,7 +219,7 @@ namespace Kyoo.Utils
foreach (T i in self) foreach (T i in self)
await action(i); await action(i);
} }
/// <summary> /// <summary>
/// A foreach used as a function with a little specificity: the list can be null. /// A foreach used as a function with a little specificity: the list can be null.
/// </summary> /// </summary>
@ -233,7 +233,7 @@ namespace Kyoo.Utils
await foreach (T i in self) await foreach (T i in self)
action(i); action(i);
} }
/// <summary> /// <summary>
/// Split a list in a small chunk of data. /// Split a list in a small chunk of data.
/// </summary> /// </summary>
@ -247,7 +247,7 @@ namespace Kyoo.Utils
for (int i = 0; i < list.Count; i += countPerList) for (int i = 0; i < list.Count; i += countPerList)
yield return list.GetRange(i, Math.Min(list.Count - i, countPerList)); yield return list.GetRange(i, Math.Min(list.Count - i, countPerList));
} }
/// <summary> /// <summary>
/// Split a list in a small chunk of data. /// Split a list in a small chunk of data.
/// </summary> /// </summary>
@ -260,7 +260,7 @@ namespace Kyoo.Utils
{ {
T[] ret = new T[countPerList]; T[] ret = new T[countPerList];
int i = 0; int i = 0;
using IEnumerator<T> enumerator = list.GetEnumerator(); using IEnumerator<T> enumerator = list.GetEnumerator();
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {

View File

@ -24,7 +24,7 @@ namespace Kyoo.Utils
/// <returns>The two list merged as an array</returns> /// <returns>The two list merged as an array</returns>
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)] [ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
public static T[] MergeLists<T>([CanBeNull] IEnumerable<T> first, public static T[] MergeLists<T>([CanBeNull] IEnumerable<T> first,
[CanBeNull] IEnumerable<T> second, [CanBeNull] IEnumerable<T> second,
[CanBeNull] Func<T, T, bool> isEqual = null) [CanBeNull] Func<T, T, bool> isEqual = null)
{ {
if (first == null) if (first == null)
@ -82,7 +82,7 @@ namespace Kyoo.Utils
{ {
bool success = first.TryAdd(key, value); bool success = first.TryAdd(key, value);
hasChanged |= success; hasChanged |= success;
if (success || first[key]?.Equals(default) == false || value?.Equals(default) != false) if (success || first[key]?.Equals(default) == false || value?.Equals(default) != false)
continue; continue;
first[key] = value; first[key] = value;
@ -150,9 +150,9 @@ namespace Kyoo.Utils
{ {
Type type = typeof(T); Type type = typeof(T);
IEnumerable<PropertyInfo> properties = type.GetProperties() IEnumerable<PropertyInfo> properties = type.GetProperties()
.Where(x => x.CanRead && x.CanWrite .Where(x => x.CanRead && x.CanWrite
&& Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null); && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
object value = property.GetValue(second); object value = property.GetValue(second);
@ -163,7 +163,7 @@ namespace Kyoo.Utils
merge.OnMerge(second); merge.OnMerge(second);
return first; return first;
} }
/// <summary> /// <summary>
/// Set every non-default values of seconds to the corresponding property of second. /// Set every non-default values of seconds to the corresponding property of second.
/// Dictionaries are handled like anonymous objects with a property per key/pair value /// Dictionaries are handled like anonymous objects with a property per key/pair value
@ -190,15 +190,15 @@ namespace Kyoo.Utils
/// <typeparam name="T">Fields of T will be completed</typeparam> /// <typeparam name="T">Fields of T will be completed</typeparam>
/// <returns><see cref="first"/></returns> /// <returns><see cref="first"/></returns>
/// <exception cref="ArgumentNullException">If first is null</exception> /// <exception cref="ArgumentNullException">If first is null</exception>
public static T Complete<T>([NotNull] T first, public static T Complete<T>([NotNull] T first,
[CanBeNull] T second, [CanBeNull] T second,
[InstantHandle] Func<PropertyInfo, bool> where = null) [InstantHandle] Func<PropertyInfo, bool> where = null)
{ {
if (first == null) if (first == null)
throw new ArgumentNullException(nameof(first)); throw new ArgumentNullException(nameof(first));
if (second == null) if (second == null)
return first; return first;
Type type = typeof(T); Type type = typeof(T);
IEnumerable<PropertyInfo> properties = type.GetProperties() IEnumerable<PropertyInfo> properties = type.GetProperties()
.Where(x => x.CanRead && x.CanWrite .Where(x => x.CanRead && x.CanWrite
@ -206,7 +206,7 @@ namespace Kyoo.Utils
if (where != null) if (where != null)
properties = properties.Where(where); properties = properties.Where(where);
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
object value = property.GetValue(second); object value = property.GetValue(second);
@ -261,7 +261,7 @@ namespace Kyoo.Utils
/// <typeparam name="T">Fields of T will be merged</typeparam> /// <typeparam name="T">Fields of T will be merged</typeparam>
/// <returns><see cref="first"/></returns> /// <returns><see cref="first"/></returns>
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)] [ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
public static T Merge<T>([CanBeNull] T first, public static T Merge<T>([CanBeNull] T first,
[CanBeNull] T second, [CanBeNull] T second,
[InstantHandle] Func<PropertyInfo, bool> where = null) [InstantHandle] Func<PropertyInfo, bool> where = null)
{ {
@ -269,21 +269,21 @@ namespace Kyoo.Utils
return second; return second;
if (second == null) if (second == null)
return first; return first;
Type type = typeof(T); Type type = typeof(T);
IEnumerable<PropertyInfo> properties = type.GetProperties() IEnumerable<PropertyInfo> properties = type.GetProperties()
.Where(x => x.CanRead && x.CanWrite .Where(x => x.CanRead && x.CanWrite
&& Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null); && Attribute.GetCustomAttribute(x, typeof(NotMergeableAttribute)) == null);
if (where != null) if (where != null)
properties = properties.Where(where); properties = properties.Where(where);
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
object oldValue = property.GetValue(first); object oldValue = property.GetValue(first);
object newValue = property.GetValue(second); object newValue = property.GetValue(second);
object defaultValue = property.PropertyType.GetClrDefault(); object defaultValue = property.PropertyType.GetClrDefault();
if (oldValue?.Equals(defaultValue) != false) if (oldValue?.Equals(defaultValue) != false)
property.SetValue(first, newValue); property.SetValue(first, newValue);
else if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>))) else if (Utility.IsOfGenericType(property.PropertyType, typeof(IDictionary<,>)))
@ -310,7 +310,7 @@ namespace Kyoo.Utils
.GenericTypeArguments .GenericTypeArguments
.First(); .First();
Func<IResource, IResource, bool> equalityComparer = enumerableType.IsAssignableTo(typeof(IResource)) Func<IResource, IResource, bool> equalityComparer = enumerableType.IsAssignableTo(typeof(IResource))
? (x, y) => x.Slug == y.Slug ? (x, y) => x.Slug == y.Slug
: null; : null;
property.SetValue(first, Utility.RunGenericMethod<object>( property.SetValue(first, Utility.RunGenericMethod<object>(
typeof(Merger), typeof(Merger),
@ -344,4 +344,4 @@ namespace Kyoo.Utils
return obj; return obj;
} }
} }
} }

View File

@ -17,7 +17,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>
@ -27,7 +27,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>
@ -37,7 +37,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>
@ -47,7 +47,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>
@ -57,7 +57,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>
@ -67,7 +67,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>
@ -77,7 +77,7 @@ namespace Kyoo.Utils
{ {
return action.Method; return action.Method;
} }
/// <summary> /// <summary>
/// Get a MethodInfo from a direct method. /// Get a MethodInfo from a direct method.
/// </summary> /// </summary>

View File

@ -28,7 +28,7 @@ namespace Kyoo.Utils
return ex.Body is MemberExpression || return ex.Body is MemberExpression ||
ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression; ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression;
} }
/// <summary> /// <summary>
/// Get the name of a property. Useful for selectors as members ex: Load(x => x.Shows) /// Get the name of a property. Useful for selectors as members ex: Load(x => x.Shows)
/// </summary> /// </summary>
@ -66,7 +66,7 @@ namespace Kyoo.Utils
_ => throw new ArgumentException($"Can't get value of a non property/field (member: {member}).") _ => throw new ArgumentException($"Can't get value of a non property/field (member: {member}).")
}; };
} }
/// <summary> /// <summary>
/// Slugify a string (Replace spaces by -, Uniformize accents é -> e) /// Slugify a string (Replace spaces by -, Uniformize accents é -> e)
/// </summary> /// </summary>
@ -78,7 +78,7 @@ namespace Kyoo.Utils
return null; return null;
str = str.ToLowerInvariant(); str = str.ToLowerInvariant();
string normalizedString = str.Normalize(NormalizationForm.FormD); string normalizedString = str.Normalize(NormalizationForm.FormD);
StringBuilder stringBuilder = new(); StringBuilder stringBuilder = new();
foreach (char c in normalizedString) foreach (char c in normalizedString)
@ -104,7 +104,7 @@ namespace Kyoo.Utils
public static object GetClrDefault(this Type type) public static object GetClrDefault(this Type type)
{ {
return type.IsValueType return type.IsValueType
? Activator.CreateInstance(type) ? Activator.CreateInstance(type)
: null; : null;
} }
@ -135,7 +135,7 @@ namespace Kyoo.Utils
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
return IsOfGenericType(obj.GetType(), genericType); return IsOfGenericType(obj.GetType(), genericType);
} }
/// <summary> /// <summary>
/// Check if <see cref="type"/> inherit from a generic type <see cref="genericType"/>. /// Check if <see cref="type"/> inherit from a generic type <see cref="genericType"/>.
/// </summary> /// </summary>
@ -201,15 +201,15 @@ namespace Kyoo.Utils
/// The list of generic parameters. /// The list of generic parameters.
/// </param> /// </param>
/// <param name="args"> /// <param name="args">
/// The list of parameters. /// The list of parameters.
/// </param> /// </param>
/// <exception cref="ArgumentException">No method match the given constraints.</exception> /// <exception cref="ArgumentException">No method match the given constraints.</exception>
/// <returns>The method handle of the matching method.</returns> /// <returns>The method handle of the matching method.</returns>
[PublicAPI] [PublicAPI]
[NotNull] [NotNull]
public static MethodInfo GetMethod([NotNull] Type type, public static MethodInfo GetMethod([NotNull] Type type,
BindingFlags flag, BindingFlags flag,
string name, string name,
[NotNull] Type[] generics, [NotNull] Type[] generics,
[NotNull] object[] args) [NotNull] object[] args)
{ {
@ -219,7 +219,7 @@ namespace Kyoo.Utils
throw new ArgumentNullException(nameof(generics)); throw new ArgumentNullException(nameof(generics));
if (args == null) if (args == null)
throw new ArgumentNullException(nameof(args)); throw new ArgumentNullException(nameof(args));
MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public) MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public)
.Where(x => x.Name == name) .Where(x => x.Name == name)
.Where(x => x.GetGenericArguments().Length == generics.Length) .Where(x => x.GetGenericArguments().Length == generics.Length)
@ -234,7 +234,7 @@ namespace Kyoo.Utils
// return x.GetGenericArguments().All(y => y.IsAssignableFrom(generics[i++])); // return x.GetGenericArguments().All(y => y.IsAssignableFrom(generics[i++]));
// }) // })
// .IfEmpty(() => throw new NullReferenceException($"No method {name} match the generics specified.")) // .IfEmpty(() => throw new NullReferenceException($"No method {name} match the generics specified."))
// TODO this won't work for Type<T> because T is specified in arguments but not in the parameters type. // TODO this won't work for Type<T> because T is specified in arguments but not in the parameters type.
// .Where(x => // .Where(x =>
// { // {
@ -249,7 +249,7 @@ namespace Kyoo.Utils
return methods[0]; return methods[0];
throw new ArgumentException($"Multiple methods named {name} match the generics and parameters constraints."); throw new ArgumentException($"Multiple methods named {name} match the generics and parameters constraints.");
} }
/// <summary> /// <summary>
/// Run a generic static method for a runtime <see cref="Type"/>. /// Run a generic static method for a runtime <see cref="Type"/>.
/// </summary> /// </summary>
@ -276,14 +276,14 @@ namespace Kyoo.Utils
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type,object[])"/> /// <seealso cref="RunGenericMethod{T}(object,string,System.Type,object[])"/>
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type[],object[])"/> /// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type[],object[])"/>
public static T RunGenericMethod<T>( public static T RunGenericMethod<T>(
[NotNull] Type owner, [NotNull] Type owner,
[NotNull] string methodName, [NotNull] string methodName,
[NotNull] Type type, [NotNull] Type type,
params object[] args) params object[] args)
{ {
return RunGenericMethod<T>(owner, methodName, new[] {type}, args); return RunGenericMethod<T>(owner, methodName, new[] { type }, args);
} }
/// <summary> /// <summary>
/// Run a generic static method for a multiple runtime <see cref="Type"/>. /// Run a generic static method for a multiple runtime <see cref="Type"/>.
/// If your generic method only needs one type, see /// If your generic method only needs one type, see
@ -313,7 +313,7 @@ namespace Kyoo.Utils
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/> /// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
[PublicAPI] [PublicAPI]
public static T RunGenericMethod<T>( public static T RunGenericMethod<T>(
[NotNull] Type owner, [NotNull] Type owner,
[NotNull] string methodName, [NotNull] string methodName,
[NotNull] Type[] types, [NotNull] Type[] types,
params object[] args) params object[] args)
@ -361,7 +361,7 @@ namespace Kyoo.Utils
[NotNull] Type type, [NotNull] Type type,
params object[] args) params object[] args)
{ {
return RunGenericMethod<T>(instance, methodName, new[] {type}, args); return RunGenericMethod<T>(instance, methodName, new[] { type }, args);
} }
/// <summary> /// <summary>
@ -392,7 +392,7 @@ namespace Kyoo.Utils
/// <seealso cref="RunGenericMethod{T}(object,string,System.Type[],object[])"/> /// <seealso cref="RunGenericMethod{T}(object,string,System.Type[],object[])"/>
/// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/> /// <seealso cref="RunGenericMethod{T}(System.Type,string,System.Type,object[])"/>
public static T RunGenericMethod<T>( public static T RunGenericMethod<T>(
[NotNull] object instance, [NotNull] object instance,
[NotNull] string methodName, [NotNull] string methodName,
[NotNull] Type[] types, [NotNull] Type[] types,
params object[] args) params object[] args)
@ -436,4 +436,4 @@ namespace Kyoo.Utils
return $"{type.Name[..type.Name.IndexOf('`')]}<{generics}>"; return $"{type.Name[..type.Name.IndexOf('`')]}<{generics}>";
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -32,10 +33,10 @@ namespace Kyoo.Authentication
{ {
/// <inheritdoc /> /// <inheritdoc />
public string Slug => "auth"; public string Slug => "auth";
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Authentication"; public string Name => "Authentication";
/// <inheritdoc /> /// <inheritdoc />
public string Description => "Enable OpenID authentication for Kyoo."; public string Description => "Enable OpenID authentication for Kyoo.";
@ -70,8 +71,10 @@ namespace Kyoo.Authentication
/// <param name="configuration">The configuration to use</param> /// <param name="configuration">The configuration to use</param>
/// <param name="logger">The logger used to allow IdentityServer to log things</param> /// <param name="logger">The logger used to allow IdentityServer to log things</param>
/// <param name="environment">The environment information to check if the app runs in debug mode</param> /// <param name="environment">The environment information to check if the app runs in debug mode</param>
[SuppressMessage("ReSharper", "ContextualLoggerProblem",
Justification = "The logger is used for a dependency that is not created via the container.")]
public AuthenticationModule(IConfiguration configuration, public AuthenticationModule(IConfiguration configuration,
ILogger<DefaultCorsPolicyService> logger, ILogger<DefaultCorsPolicyService> logger,
IWebHostEnvironment environment) IWebHostEnvironment environment)
{ {
_configuration = configuration; _configuration = configuration;
@ -100,16 +103,16 @@ namespace Kyoo.Authentication
IdentityModelEventSource.ShowPII = true; IdentityModelEventSource.ShowPII = true;
services.AddControllers(); services.AddControllers();
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos) // TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
// TODO Check if tokens should be stored. // TODO Check if tokens should be stored.
List<Client> clients = new(); List<Client> clients = new();
_configuration.GetSection("authentication:clients").Bind(clients); _configuration.GetSection("authentication:clients").Bind(clients);
CertificateOption certificateOptions = new(); CertificateOption certificateOptions = new();
_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions); _configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
clients.AddRange(IdentityContext.GetClients()); clients.AddRange(IdentityContext.GetClients());
foreach (Client client in clients) foreach (Client client in clients)
{ {
@ -131,7 +134,7 @@ namespace Kyoo.Authentication
.AddInMemoryClients(clients) .AddInMemoryClients(clients)
.AddProfileService<AccountApi>() .AddProfileService<AccountApi>()
.AddSigninKeys(certificateOptions); .AddSigninKeys(certificateOptions);
services.AddAuthentication() services.AddAuthentication()
.AddJwtBearer(options => .AddJwtBearer(options =>
{ {
@ -181,4 +184,4 @@ namespace Kyoo.Authentication
SA.New<IApplicationBuilder>(app => app.UseAuthorization(), SA.Authorization) SA.New<IApplicationBuilder>(app => app.UseAuthorization(), SA.Authorization)
}; };
} }
} }

View File

@ -28,12 +28,12 @@ namespace Kyoo.Authentication
/// <param name="builder">The identity server that will be modified.</param> /// <param name="builder">The identity server that will be modified.</param>
/// <param name="options">The certificate options</param> /// <param name="options">The certificate options</param>
/// <returns></returns> /// <returns></returns>
public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder, public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder,
CertificateOption options) CertificateOption options)
{ {
X509Certificate2 certificate = GetCertificate(options); X509Certificate2 certificate = GetCertificate(options);
builder.AddSigningCredential(certificate); builder.AddSigningCredential(certificate);
if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow) if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow)
{ {
Console.WriteLine("Signin certificate will expire soon, renewing it."); Console.WriteLine("Signin certificate will expire soon, renewing it.");
@ -54,8 +54,8 @@ namespace Kyoo.Authentication
/// <returns>A valid certificate</returns> /// <returns>A valid certificate</returns>
private static X509Certificate2 GetCertificate(CertificateOption options) private static X509Certificate2 GetCertificate(CertificateOption options)
{ {
return File.Exists(options.File) return File.Exists(options.File)
? GetExistingCredential(options.File, options.Password) ? GetExistingCredential(options.File, options.Password)
: GenerateCertificate(options.File, options.Password); : GenerateCertificate(options.File, options.Password);
} }
@ -83,19 +83,19 @@ namespace Kyoo.Authentication
private static X509Certificate2 GenerateCertificate(string file, string password) private static X509Certificate2 GenerateCertificate(string file, string password)
{ {
SecureRandom random = new(); SecureRandom random = new();
X509V3CertificateGenerator certificateGenerator = new(); X509V3CertificateGenerator certificateGenerator = new();
certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One,
BigInteger.ValueOf(long.MaxValue), random)); BigInteger.ValueOf(long.MaxValue), random));
certificateGenerator.SetIssuerDN(new X509Name($"C=NL, O=SDG, CN=Kyoo")); certificateGenerator.SetIssuerDN(new X509Name($"C=NL, O=SDG, CN=Kyoo"));
certificateGenerator.SetSubjectDN(new X509Name($"C=NL, O=SDG, CN=Kyoo")); certificateGenerator.SetSubjectDN(new X509Name($"C=NL, O=SDG, CN=Kyoo"));
certificateGenerator.SetNotBefore(DateTime.UtcNow.Date); certificateGenerator.SetNotBefore(DateTime.UtcNow.Date);
certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddMonths(3)); certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddMonths(3));
KeyGenerationParameters keyGenerationParameters = new(random, 2048); KeyGenerationParameters keyGenerationParameters = new(random, 2048);
RsaKeyPairGenerator keyPairGenerator = new(); RsaKeyPairGenerator keyPairGenerator = new();
keyPairGenerator.Init(keyGenerationParameters); keyPairGenerator.Init(keyGenerationParameters);
AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair(); AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public); certificateGenerator.SetPublicKey(subjectKeyPair.Public);
@ -104,7 +104,7 @@ namespace Kyoo.Authentication
X509Certificate bouncyCert = certificateGenerator.Generate(signatureFactory); X509Certificate bouncyCert = certificateGenerator.Generate(signatureFactory);
Pkcs12Store store = new Pkcs12StoreBuilder().Build(); Pkcs12Store store = new Pkcs12StoreBuilder().Build();
store.SetKeyEntry("Kyoo_key", new AsymmetricKeyEntry(subjectKeyPair.Private), new [] store.SetKeyEntry("Kyoo_key", new AsymmetricKeyEntry(subjectKeyPair.Private), new[]
{ {
new X509CertificateEntry(bouncyCert) new X509CertificateEntry(bouncyCert)
}); });

View File

@ -15,7 +15,7 @@ namespace Kyoo.Authentication
{ {
/// <summary> /// <summary>
/// A permission validator to validate permission with user Permission array /// A permission validator to validate permission with user Permission array
/// or the default array from the configurations if the user is not logged. /// or the default array from the configurations if the user is not logged.
/// </summary> /// </summary>
public class PermissionValidatorFactory : IPermissionValidator public class PermissionValidatorFactory : IPermissionValidator
{ {
@ -38,7 +38,7 @@ namespace Kyoo.Authentication
{ {
return new PermissionValidator(attribute.Type, attribute.Kind, attribute.Group, _options); return new PermissionValidator(attribute.Type, attribute.Kind, attribute.Group, _options);
} }
/// <inheritdoc /> /// <inheritdoc />
public IFilterMetadata Create(PartialPermissionAttribute attribute) public IFilterMetadata Create(PartialPermissionAttribute attribute)
{ {
@ -149,4 +149,4 @@ namespace Kyoo.Authentication
} }
} }
} }
} }

View File

@ -37,7 +37,7 @@ namespace Kyoo.Authentication
return new(user.ID.ToString()) return new(user.ID.ToString())
{ {
DisplayName = user.Username, DisplayName = user.Username,
AdditionalClaims = new[] {new Claim("permissions", string.Join(',', user.Permissions))} AdditionalClaims = new[] { new Claim("permissions", string.Join(',', user.Permissions)) }
}; };
} }

View File

@ -13,13 +13,13 @@ namespace Kyoo.Authentication.Models.DTO
/// </summary> /// </summary>
[EmailAddress(ErrorMessage = "The email is invalid.")] [EmailAddress(ErrorMessage = "The email is invalid.")]
public string Email { get; set; } public string Email { get; set; }
/// <summary> /// <summary>
/// The new username of the user. /// The new username of the user.
/// </summary> /// </summary>
[MinLength(4, ErrorMessage = "The username must have at least 4 characters")] [MinLength(4, ErrorMessage = "The username must have at least 4 characters")]
public string Username { get; set; } public string Username { get; set; }
/// <summary> /// <summary>
/// The picture icon. /// The picture icon.
/// </summary> /// </summary>

View File

@ -9,17 +9,17 @@ namespace Kyoo.Authentication.Models.DTO
/// The user's username. /// The user's username.
/// </summary> /// </summary>
public string Username { get; set; } public string Username { get; set; }
/// <summary> /// <summary>
/// The user's password. /// The user's password.
/// </summary> /// </summary>
public string Password { get; set; } public string Password { get; set; }
/// <summary> /// <summary>
/// Should the user stay logged in? If true a cookie will be put. /// Should the user stay logged in? If true a cookie will be put.
/// </summary> /// </summary>
public bool StayLoggedIn { get; set; } public bool StayLoggedIn { get; set; }
/// <summary> /// <summary>
/// The return url of the login flow. /// The return url of the login flow.
/// </summary> /// </summary>

View File

@ -9,7 +9,7 @@ namespace Kyoo.Authentication.Models.DTO
/// The One Time Access Code /// The One Time Access Code
/// </summary> /// </summary>
public string Otac { get; set; } public string Otac { get; set; }
/// <summary> /// <summary>
/// Should the user stay logged /// Should the user stay logged
/// </summary> /// </summary>

View File

@ -15,13 +15,13 @@ namespace Kyoo.Authentication.Models.DTO
/// </summary> /// </summary>
[EmailAddress(ErrorMessage = "The email must be a valid email address")] [EmailAddress(ErrorMessage = "The email must be a valid email address")]
public string Email { get; set; } public string Email { get; set; }
/// <summary> /// <summary>
/// The user's username. /// The user's username.
/// </summary> /// </summary>
[MinLength(4, ErrorMessage = "The username must have at least {1} characters")] [MinLength(4, ErrorMessage = "The username must have at least {1} characters")]
public string Username { get; set; } public string Username { get; set; }
/// <summary> /// <summary>
/// The user's password. /// The user's password.
/// </summary> /// </summary>
@ -44,5 +44,5 @@ namespace Kyoo.Authentication.Models.DTO
ExtraData = new Dictionary<string, string>() ExtraData = new Dictionary<string, string>()
}; };
} }
} }
} }

View File

@ -42,11 +42,11 @@ namespace Kyoo.Authentication
AllowedGrantTypes = GrantTypes.Code, AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true, RequirePkce = true,
RequireClientSecret = false, RequireClientSecret = false,
AllowAccessTokensViaBrowser = true, AllowAccessTokensViaBrowser = true,
AllowOfflineAccess = true, AllowOfflineAccess = true,
RequireConsent = false, RequireConsent = false,
AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" }, AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" },
RedirectUris = { "/", "/silent.html" }, RedirectUris = { "/", "/silent.html" },
PostLogoutRedirectUris = { "/logout" } PostLogoutRedirectUris = { "/logout" }
@ -84,7 +84,7 @@ namespace Kyoo.Authentication
} }
}; };
} }
/// <summary> /// <summary>
/// The list of APIs (this is used to create Audiences) /// The list of APIs (this is used to create Audiences)
/// </summary> /// </summary>

View File

@ -14,12 +14,12 @@ namespace Kyoo.Authentication.Models
/// The options for certificates /// The options for certificates
/// </summary> /// </summary>
public CertificateOption Certificate { get; set; } public CertificateOption Certificate { get; set; }
/// <summary> /// <summary>
/// Options for permissions /// Options for permissions
/// </summary> /// </summary>
public PermissionOption Permissions { get; set; } public PermissionOption Permissions { get; set; }
/// <summary> /// <summary>
/// Root path of user's profile pictures. /// Root path of user's profile pictures.
/// </summary> /// </summary>

View File

@ -9,7 +9,7 @@ namespace Kyoo.Authentication.Models
/// The path to get this option from the root configuration. /// The path to get this option from the root configuration.
/// </summary> /// </summary>
public const string Path = "authentication:certificate"; public const string Path = "authentication:certificate";
/// <summary> /// <summary>
/// The path of the certificate file. /// The path of the certificate file.
/// </summary> /// </summary>

View File

@ -14,7 +14,7 @@ namespace Kyoo.Authentication.Models
/// The default permissions that will be given to a non-connected user. /// The default permissions that will be given to a non-connected user.
/// </summary> /// </summary>
public string[] Default { get; set; } public string[] Default { get; set; }
/// <summary> /// <summary>
/// Permissions applied to a new user. /// Permissions applied to a new user.
/// </summary> /// </summary>

View File

@ -57,8 +57,8 @@ namespace Kyoo.Authentication.Views
_files = files; _files = files;
_options = options; _options = options;
} }
/// <summary> /// <summary>
/// Register a new user and return a OTAC to connect to it. /// Register a new user and return a OTAC to connect to it.
/// </summary> /// </summary>
@ -78,10 +78,10 @@ namespace Kyoo.Authentication.Views
} }
catch (DuplicatedItemException) catch (DuplicatedItemException)
{ {
return Conflict(new {Errors = new {Duplicate = new[] {"A user with this name already exists"}}}); return Conflict(new { Errors = new { Duplicate = new[] { "A user with this name already exists" } } });
} }
return Ok(new {Otac = user.ExtraData["otac"]}); return Ok(new { Otac = user.ExtraData["otac"] });
} }
/// <summary> /// <summary>
@ -99,7 +99,7 @@ namespace Kyoo.Authentication.Views
ExpiresUtc = DateTimeOffset.UtcNow.AddMonths(1) ExpiresUtc = DateTimeOffset.UtcNow.AddMonths(1)
}; };
} }
/// <summary> /// <summary>
/// Login the user. /// Login the user.
/// </summary> /// </summary>
@ -117,7 +117,7 @@ namespace Kyoo.Authentication.Views
await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(login.StayLoggedIn)); await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(login.StayLoggedIn));
return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true }); return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true });
} }
/// <summary> /// <summary>
/// Use a OTAC to login a user. /// Use a OTAC to login a user.
/// </summary> /// </summary>
@ -127,22 +127,23 @@ namespace Kyoo.Authentication.Views
{ {
// TODO once hstore (Dictionary<string, string> accessor) are supported, use them. // TODO once hstore (Dictionary<string, string> accessor) are supported, use them.
// We retrieve all users, this is inefficient. // We retrieve all users, this is inefficient.
User user = (await _users.GetAll()).FirstOrDefault(x => x.ExtraData.GetValueOrDefault("otac") == otac.Otac); User user = (await _users.GetAll()).FirstOrDefault(x => x.ExtraData.GetValueOrDefault("otac") == otac.Otac);
if (user == null) if (user == null)
return Unauthorized(); return Unauthorized();
if (DateTime.ParseExact(user.ExtraData["otac-expire"], "s", CultureInfo.InvariantCulture) <= if (DateTime.ParseExact(user.ExtraData["otac-expire"], "s", CultureInfo.InvariantCulture) <=
DateTime.UtcNow) DateTime.UtcNow)
{ {
return BadRequest(new return BadRequest(new
{ {
code = "ExpiredOTAC", description = "The OTAC has expired. Try to login with your password." code = "ExpiredOTAC",
description = "The OTAC has expired. Try to login with your password."
}); });
} }
await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(otac.StayLoggedIn)); await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(otac.StayLoggedIn));
return Ok(); return Ok();
} }
/// <summary> /// <summary>
/// Sign out an user /// Sign out an user
/// </summary> /// </summary>
@ -170,7 +171,7 @@ namespace Kyoo.Authentication.Views
User user = await _users.GetOrDefault(int.Parse(context.Subject.GetSubjectId())); User user = await _users.GetOrDefault(int.Parse(context.Subject.GetSubjectId()));
context.IsActive = user != null; context.IsActive = user != null;
} }
/// <summary> /// <summary>
/// Get the user's profile picture. /// Get the user's profile picture.
/// </summary> /// </summary>
@ -185,7 +186,7 @@ namespace Kyoo.Authentication.Views
string path = Path.Combine(_options.Value.ProfilePicturePath, user.ID.ToString()); string path = Path.Combine(_options.Value.ProfilePicturePath, user.ID.ToString());
return _files.FileResult(path); return _files.FileResult(path);
} }
/// <summary> /// <summary>
/// Update profile information (email, username, profile picture...) /// Update profile information (email, username, profile picture...)
/// </summary> /// </summary>

View File

@ -32,7 +32,7 @@ namespace Kyoo.Core
/// Should the application restart after a shutdown? /// Should the application restart after a shutdown?
/// </summary> /// </summary>
private bool _shouldRestart; private bool _shouldRestart;
/// <summary> /// <summary>
/// The cancellation token source used to allow the app to be shutdown or restarted. /// The cancellation token source used to allow the app to be shutdown or restarted.
/// </summary> /// </summary>
@ -48,7 +48,7 @@ namespace Kyoo.Core
/// </summary> /// </summary>
private ILogger _logger; private ILogger _logger;
/// <summary> /// <summary>
/// Create a new <see cref="Application"/> that will use the specified environment. /// Create a new <see cref="Application"/> that will use the specified environment.
/// </summary> /// </summary>
@ -80,28 +80,28 @@ namespace Kyoo.Core
public async Task Start(string[] args, Action<ContainerBuilder> configure) public async Task Start(string[] args, Action<ContainerBuilder> configure)
{ {
_dataDir = _SetupDataDir(args); _dataDir = _SetupDataDir(args);
LoggerConfiguration config = new(); LoggerConfiguration config = new();
_ConfigureLogging(config, null, null); _ConfigureLogging(config, null, null);
Log.Logger = config.CreateBootstrapLogger(); Log.Logger = config.CreateBootstrapLogger();
_logger = Log.Logger.ForContext<Application>(); _logger = Log.Logger.ForContext<Application>();
AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush(); AppDomain.CurrentDomain.ProcessExit += (_, _) => Log.CloseAndFlush();
AppDomain.CurrentDomain.UnhandledException += (_, ex) AppDomain.CurrentDomain.UnhandledException += (_, ex)
=> Log.Fatal(ex.ExceptionObject as Exception, "Unhandled exception"); => Log.Fatal(ex.ExceptionObject as Exception, "Unhandled exception");
do do
{ {
IHost host = _CreateWebHostBuilder(args) IHost host = _CreateWebHostBuilder(args)
.ConfigureContainer(configure) .ConfigureContainer(configure)
.Build(); .Build();
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
await _StartWithHost(host, _tokenSource.Token); await _StartWithHost(host, _tokenSource.Token);
} }
while (_shouldRestart); while (_shouldRestart);
} }
/// <inheritdoc /> /// <inheritdoc />
public void Shutdown() public void Shutdown()
{ {
@ -155,7 +155,7 @@ namespace Kyoo.Core
.AddCommandLine(args) .AddCommandLine(args)
.Build(); .Build();
string path = parsed.GetValue<string>("datadir") string path = parsed.GetValue<string>("datadir")
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Kyoo"); ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Kyoo");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
@ -165,7 +165,7 @@ namespace Kyoo.Core
if (!File.Exists(GetConfigFile())) if (!File.Exists(GetConfigFile()))
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, GetConfigFile()), File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, GetConfigFile()),
GetConfigFile()); GetConfigFile());
return path; return path;
} }
@ -217,7 +217,7 @@ namespace Kyoo.Core
.UseStartup(host => PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog())) .UseStartup(host => PluginsStartup.FromWebHost(host, new LoggerFactory().AddSerilog()))
); );
} }
/// <summary> /// <summary>
/// Register settings.json, environment variables and command lines arguments as configuration. /// Register settings.json, environment variables and command lines arguments as configuration.
/// </summary> /// </summary>

View File

@ -17,7 +17,7 @@ namespace Kyoo.Core.Controllers
public class ConfigurationManager : IConfigurationManager public class ConfigurationManager : IConfigurationManager
{ {
/// <summary> /// <summary>
/// The configuration to retrieve and edit. /// The configuration to retrieve and edit.
/// </summary> /// </summary>
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
@ -58,7 +58,7 @@ namespace Kyoo.Core.Controllers
ConfigurationReference config = ConfigurationReference.CreateUntyped(path); ConfigurationReference config = ConfigurationReference.CreateUntyped(path);
_references.Add(config.Path, config.Type); _references.Add(config.Path, config.Type);
} }
/// <inheritdoc /> /// <inheritdoc />
public void Register(string path, Type type) public void Register(string path, Type type)
{ {
@ -98,7 +98,7 @@ namespace Kyoo.Core.Controllers
throw new ArgumentException($"The configuration at {path} is not editable or readable."); throw new ArgumentException($"The configuration at {path} is not editable or readable.");
throw new ItemNotFoundException($"No configuration exists for the name: {path}"); throw new ItemNotFoundException($"No configuration exists for the name: {path}");
} }
/// <inheritdoc /> /// <inheritdoc />
public object GetValue(string path) public object GetValue(string path)
{ {
@ -124,7 +124,7 @@ namespace Kyoo.Core.Controllers
$"a resource of type {type.Name}."); $"a resource of type {type.Name}.");
return (T)GetValue(path); return (T)GetValue(path);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task EditValue(string path, object value) public async Task EditValue(string path, object value)
{ {
@ -133,7 +133,7 @@ namespace Kyoo.Core.Controllers
value = JObject.FromObject(value).ToObject(type); value = JObject.FromObject(value).ToObject(type);
if (value == null) if (value == null)
throw new ArgumentException("Invalid value format."); throw new ArgumentException("Invalid value format.");
ExpandoObject config = _ToObject(_configuration); ExpandoObject config = _ToObject(_configuration);
IDictionary<string, object> configDic = config; IDictionary<string, object> configDic = config;
configDic[path] = value; configDic[path] = value;
@ -141,7 +141,7 @@ namespace Kyoo.Core.Controllers
await using StreamWriter writer = new(_application.GetConfigFile()); await using StreamWriter writer = new(_application.GetConfigFile());
await writer.WriteAsync(obj.ToString()); await writer.WriteAsync(obj.ToString());
} }
/// <summary> /// <summary>
/// Transform a configuration to a strongly typed object (the root configuration is an <see cref="ExpandoObject"/> /// Transform a configuration to a strongly typed object (the root configuration is an <see cref="ExpandoObject"/>
/// but child elements are using strong types. /// but child elements are using strong types.
@ -169,7 +169,7 @@ namespace Kyoo.Core.Controllers
continue; continue;
} }
} }
return obj; return obj;
} }
@ -192,4 +192,4 @@ namespace Kyoo.Core.Controllers
return obj; return obj;
} }
} }
} }

View File

@ -31,12 +31,12 @@ namespace Kyoo.Core.Controllers
/// (only if the option is set to metadata in show) /// (only if the option is set to metadata in show)
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
/// <summary> /// <summary>
/// Options to check if the metadata should be kept in the show directory or in a kyoo's directory. /// Options to check if the metadata should be kept in the show directory or in a kyoo's directory.
/// </summary> /// </summary>
private readonly IOptionsMonitor<BasicOptions> _options; private readonly IOptionsMonitor<BasicOptions> _options;
/// <summary> /// <summary>
/// Create a new <see cref="FileSystemComposite"/> from a list of <see cref="IFileSystem"/> mapped to their /// Create a new <see cref="FileSystemComposite"/> from a list of <see cref="IFileSystem"/> mapped to their
/// metadata. /// metadata.
@ -45,7 +45,7 @@ namespace Kyoo.Core.Controllers
/// <param name="libraryManager">The library manager used to load shows to retrieve their path.</param> /// <param name="libraryManager">The library manager used to load shows to retrieve their path.</param>
/// <param name="options">The options to use.</param> /// <param name="options">The options to use.</param>
public FileSystemComposite(ICollection<Meta<Func<IFileSystem>, FileSystemMetadataAttribute>> fileSystems, public FileSystemComposite(ICollection<Meta<Func<IFileSystem>, FileSystemMetadataAttribute>> fileSystems,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IOptionsMonitor<BasicOptions> options) IOptionsMonitor<BasicOptions> options)
{ {
_fileSystems = fileSystems; _fileSystems = fileSystems;
@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
_options = options; _options = options;
} }
/// <summary> /// <summary>
/// Retrieve the file system that should be used for a given path. /// Retrieve the file system that should be used for a given path.
/// </summary> /// </summary>
@ -159,7 +159,7 @@ namespace Kyoo.Core.Controllers
return _GetFileSystemForPath(path, out string relativePath) return _GetFileSystemForPath(path, out string relativePath)
.Exists(relativePath); .Exists(relativePath);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<string> GetExtraDirectory<T>(T resource) public async Task<string> GetExtraDirectory<T>(T resource)
{ {
@ -191,7 +191,7 @@ namespace Kyoo.Core.Controllers
Season season => await GetExtraDirectory(season.Show), Season season => await GetExtraDirectory(season.Show),
Episode episode => await GetExtraDirectory(episode.Show), Episode episode => await GetExtraDirectory(episode.Show),
Track track => await GetExtraDirectory(track.Episode), Track track => await GetExtraDirectory(track.Episode),
IResource res => Combine(_options.CurrentValue.MetadataPath, IResource res => Combine(_options.CurrentValue.MetadataPath,
typeof(T).Name.ToLowerInvariant(), res.Slug), typeof(T).Name.ToLowerInvariant(), res.Slug),
_ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLowerInvariant()) _ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLowerInvariant())
}; };

View File

@ -14,7 +14,7 @@ namespace Kyoo.Core.Controllers
/// <summary> /// <summary>
/// A <see cref="IFileSystem"/> for http/https links. /// A <see cref="IFileSystem"/> for http/https links.
/// </summary> /// </summary>
[FileSystemMetadata(new [] {"http", "https"})] [FileSystemMetadata(new[] { "http", "https" })]
public class HttpFileSystem : IFileSystem public class HttpFileSystem : IFileSystem
{ {
/// <summary> /// <summary>
@ -30,8 +30,8 @@ namespace Kyoo.Core.Controllers
{ {
_clientFactory = factory; _clientFactory = factory;
} }
/// <inheritdoc /> /// <inheritdoc />
public IActionResult FileResult(string path, bool rangeSupport = false, string type = null) public IActionResult FileResult(string path, bool rangeSupport = false, string type = null)
{ {
@ -46,7 +46,7 @@ namespace Kyoo.Core.Controllers
HttpClient client = _clientFactory.CreateClient(); HttpClient client = _clientFactory.CreateClient();
return client.GetStreamAsync(path); return client.GetStreamAsync(path);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<Stream> GetReader(string path, AsyncRef<string> mime) public async Task<Stream> GetReader(string path, AsyncRef<string> mime)
{ {

View File

@ -15,7 +15,7 @@ namespace Kyoo.Core.Controllers
/// <summary> /// <summary>
/// A <see cref="IFileSystem"/> for the local filesystem (using System.IO). /// A <see cref="IFileSystem"/> for the local filesystem (using System.IO).
/// </summary> /// </summary>
[FileSystemMetadata(new [] {"", "file"}, StripScheme = true)] [FileSystemMetadata(new[] { "", "file" }, StripScheme = true)]
public class LocalFileSystem : IFileSystem public class LocalFileSystem : IFileSystem
{ {
/// <summary> /// <summary>
@ -51,7 +51,7 @@ namespace Kyoo.Core.Controllers
return contentType; return contentType;
throw new NotImplementedException($"Can't get the content type of the file at: {path}"); throw new NotImplementedException($"Can't get the content type of the file at: {path}");
} }
/// <inheritdoc /> /// <inheritdoc />
public IActionResult FileResult(string path, bool rangeSupport = false, string type = null) public IActionResult FileResult(string path, bool rangeSupport = false, string type = null)
{ {
@ -72,7 +72,7 @@ namespace Kyoo.Core.Controllers
throw new ArgumentNullException(nameof(path)); throw new ArgumentNullException(nameof(path));
return Task.FromResult<Stream>(File.OpenRead(path)); return Task.FromResult<Stream>(File.OpenRead(path));
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<Stream> GetReader(string path, AsyncRef<string> mime) public Task<Stream> GetReader(string path, AsyncRef<string> mime)
{ {
@ -99,19 +99,19 @@ namespace Kyoo.Core.Controllers
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
return Task.FromResult(path); return Task.FromResult(path);
} }
/// <inheritdoc /> /// <inheritdoc />
public string Combine(params string[] paths) public string Combine(params string[] paths)
{ {
return Path.Combine(paths); return Path.Combine(paths);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<string>> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly) public Task<ICollection<string>> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly)
{ {
if (path == null) if (path == null)
throw new ArgumentNullException(nameof(path)); throw new ArgumentNullException(nameof(path));
string[] ret = Directory.Exists(path) string[] ret = Directory.Exists(path)
? Directory.GetFiles(path, "*", options) ? Directory.GetFiles(path, "*", options)
: Array.Empty<string>(); : Array.Empty<string>();
return Task.FromResult<ICollection<string>>(ret); return Task.FromResult<ICollection<string>>(ret);
@ -122,7 +122,7 @@ namespace Kyoo.Core.Controllers
{ {
return Task.FromResult(File.Exists(path) || Directory.Exists(path)); return Task.FromResult(File.Exists(path) || Directory.Exists(path));
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<string> GetExtraDirectory<T>(T resource) public Task<string> GetExtraDirectory<T>(T resource)
{ {

View File

@ -16,7 +16,7 @@ namespace Kyoo.Core.Controllers
/// The list of repositories /// The list of repositories
/// </summary> /// </summary>
private readonly IBaseRepository[] _repositories; private readonly IBaseRepository[] _repositories;
/// <inheritdoc /> /// <inheritdoc />
public ILibraryRepository LibraryRepository { get; } public ILibraryRepository LibraryRepository { get; }
/// <inheritdoc /> /// <inheritdoc />
@ -41,8 +41,8 @@ namespace Kyoo.Core.Controllers
public IProviderRepository ProviderRepository { get; } public IProviderRepository ProviderRepository { get; }
/// <inheritdoc /> /// <inheritdoc />
public IUserRepository UserRepository { get; } public IUserRepository UserRepository { get; }
/// <summary> /// <summary>
/// Create a new <see cref="LibraryManager"/> instance with every repository available. /// Create a new <see cref="LibraryManager"/> instance with every repository available.
/// </summary> /// </summary>
@ -82,7 +82,7 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<T> Get<T>(string slug) public Task<T> Get<T>(string slug)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().Get(slug); return GetRepository<T>().Get(slug);
@ -120,19 +120,19 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<T> GetOrDefault<T>(int id) public async Task<T> GetOrDefault<T>(int id)
where T : class, IResource where T : class, IResource
{ {
return await GetRepository<T>().GetOrDefault(id); return await GetRepository<T>().GetOrDefault(id);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<T> GetOrDefault<T>(string slug) public async Task<T> GetOrDefault<T>(string slug)
where T : class, IResource where T : class, IResource
{ {
return await GetRepository<T>().GetOrDefault(slug); return await GetRepository<T>().GetOrDefault(slug);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<T> GetOrDefault<T>(Expression<Func<T, bool>> where) public async Task<T> GetOrDefault<T>(Expression<Func<T, bool>> where)
where T : class, IResource where T : class, IResource
@ -145,19 +145,19 @@ namespace Kyoo.Core.Controllers
{ {
return await SeasonRepository.GetOrDefault(showID, seasonNumber); return await SeasonRepository.GetOrDefault(showID, seasonNumber);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<Season> GetOrDefault(string showSlug, int seasonNumber) public async Task<Season> GetOrDefault(string showSlug, int seasonNumber)
{ {
return await SeasonRepository.GetOrDefault(showSlug, seasonNumber); return await SeasonRepository.GetOrDefault(showSlug, seasonNumber);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber) public async Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
{ {
return await EpisodeRepository.GetOrDefault(showID, seasonNumber, episodeNumber); return await EpisodeRepository.GetOrDefault(showID, seasonNumber, episodeNumber);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber) public async Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
{ {
@ -173,9 +173,9 @@ namespace Kyoo.Core.Controllers
/// <param name="inverse">A setter function to store the owner of a releated object loaded</param> /// <param name="inverse">A setter function to store the owner of a releated object loaded</param>
/// <typeparam name="T1">The type of the owner object</typeparam> /// <typeparam name="T1">The type of the owner object</typeparam>
/// <typeparam name="T2">The type of the related object</typeparam> /// <typeparam name="T2">The type of the related object</typeparam>
private static async Task SetRelation<T1, T2>(T1 obj, private static async Task SetRelation<T1, T2>(T1 obj,
Task<ICollection<T2>> loader, Task<ICollection<T2>> loader,
Action<T1, ICollection<T2>> setter, Action<T1, ICollection<T2>> setter,
Action<T2, T1> inverse) Action<T2, T1> inverse)
{ {
ICollection<T2> loaded = await loader; ICollection<T2> loaded = await loader;
@ -230,61 +230,61 @@ namespace Kyoo.Core.Controllers
(Library l, nameof(Library.Providers)) => ProviderRepository (Library l, nameof(Library.Providers)) => ProviderRepository
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID)) .GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
.Then(x => l.Providers = x), .Then(x => l.Providers = x),
(Library l, nameof(Library.Shows)) => ShowRepository (Library l, nameof(Library.Shows)) => ShowRepository
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID)) .GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
.Then(x => l.Shows = x), .Then(x => l.Shows = x),
(Library l, nameof(Library.Collections)) => CollectionRepository (Library l, nameof(Library.Collections)) => CollectionRepository
.GetAll(x => x.Libraries.Any(y => y.ID == obj.ID)) .GetAll(x => x.Libraries.Any(y => y.ID == obj.ID))
.Then(x => l.Collections = x), .Then(x => l.Collections = x),
(Collection c, nameof(Collection.ExternalIDs)) => SetRelation(c, (Collection c, nameof(Collection.ExternalIDs)) => SetRelation(c,
ProviderRepository.GetMetadataID<Collection>(x => x.ResourceID == obj.ID), ProviderRepository.GetMetadataID<Collection>(x => x.ResourceID == obj.ID),
(x, y) => x.ExternalIDs = y, (x, y) => x.ExternalIDs = y,
(x, y) => { x.ResourceID = y.ID; }), (x, y) => { x.ResourceID = y.ID; }),
(Collection c, nameof(Collection.Shows)) => ShowRepository (Collection c, nameof(Collection.Shows)) => ShowRepository
.GetAll(x => x.Collections.Any(y => y.ID == obj.ID)) .GetAll(x => x.Collections.Any(y => y.ID == obj.ID))
.Then(x => c.Shows = x), .Then(x => c.Shows = x),
(Collection c, nameof(Collection.Libraries)) => LibraryRepository (Collection c, nameof(Collection.Libraries)) => LibraryRepository
.GetAll(x => x.Collections.Any(y => y.ID == obj.ID)) .GetAll(x => x.Collections.Any(y => y.ID == obj.ID))
.Then(x => c.Libraries = x), .Then(x => c.Libraries = x),
(Show s, nameof(Show.ExternalIDs)) => SetRelation(s, (Show s, nameof(Show.ExternalIDs)) => SetRelation(s,
ProviderRepository.GetMetadataID<Show>(x => x.ResourceID == obj.ID), ProviderRepository.GetMetadataID<Show>(x => x.ResourceID == obj.ID),
(x, y) => x.ExternalIDs = y, (x, y) => x.ExternalIDs = y,
(x, y) => { x.ResourceID = y.ID; }), (x, y) => { x.ResourceID = y.ID; }),
(Show s, nameof(Show.Genres)) => GenreRepository (Show s, nameof(Show.Genres)) => GenreRepository
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID)) .GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
.Then(x => s.Genres = x), .Then(x => s.Genres = x),
(Show s, nameof(Show.People)) => PeopleRepository (Show s, nameof(Show.People)) => PeopleRepository
.GetFromShow(obj.ID) .GetFromShow(obj.ID)
.Then(x => s.People = x), .Then(x => s.People = x),
(Show s, nameof(Show.Seasons)) => SetRelation(s, (Show s, nameof(Show.Seasons)) => SetRelation(s,
SeasonRepository.GetAll(x => x.Show.ID == obj.ID), SeasonRepository.GetAll(x => x.Show.ID == obj.ID),
(x, y) => x.Seasons = y, (x, y) => x.Seasons = y,
(x, y) => { x.Show = y; x.ShowID = y.ID; }), (x, y) => { x.Show = y; x.ShowID = y.ID; }),
(Show s, nameof(Show.Episodes)) => SetRelation(s, (Show s, nameof(Show.Episodes)) => SetRelation(s,
EpisodeRepository.GetAll(x => x.Show.ID == obj.ID), EpisodeRepository.GetAll(x => x.Show.ID == obj.ID),
(x, y) => x.Episodes = y, (x, y) => x.Episodes = y,
(x, y) => { x.Show = y; x.ShowID = y.ID; }), (x, y) => { x.Show = y; x.ShowID = y.ID; }),
(Show s, nameof(Show.Libraries)) => LibraryRepository (Show s, nameof(Show.Libraries)) => LibraryRepository
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID)) .GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
.Then(x => s.Libraries = x), .Then(x => s.Libraries = x),
(Show s, nameof(Show.Collections)) => CollectionRepository (Show s, nameof(Show.Collections)) => CollectionRepository
.GetAll(x => x.Shows.Any(y => y.ID == obj.ID)) .GetAll(x => x.Shows.Any(y => y.ID == obj.ID))
.Then(x => s.Collections = x), .Then(x => s.Collections = x),
(Show s, nameof(Show.Studio)) => StudioRepository (Show s, nameof(Show.Studio)) => StudioRepository
.GetOrDefault(x => x.Shows.Any(y => y.ID == obj.ID)) .GetOrDefault(x => x.Shows.Any(y => y.ID == obj.ID))
.Then(x => .Then(x =>
@ -292,18 +292,18 @@ namespace Kyoo.Core.Controllers
s.Studio = x; s.Studio = x;
s.StudioID = x?.ID ?? 0; s.StudioID = x?.ID ?? 0;
}), }),
(Season s, nameof(Season.ExternalIDs)) => SetRelation(s, (Season s, nameof(Season.ExternalIDs)) => SetRelation(s,
ProviderRepository.GetMetadataID<Season>(x => x.ResourceID == obj.ID), ProviderRepository.GetMetadataID<Season>(x => x.ResourceID == obj.ID),
(x, y) => x.ExternalIDs = y, (x, y) => x.ExternalIDs = y,
(x, y) => { x.ResourceID = y.ID; }), (x, y) => { x.ResourceID = y.ID; }),
(Season s, nameof(Season.Episodes)) => SetRelation(s, (Season s, nameof(Season.Episodes)) => SetRelation(s,
EpisodeRepository.GetAll(x => x.Season.ID == obj.ID), EpisodeRepository.GetAll(x => x.Season.ID == obj.ID),
(x, y) => x.Episodes = y, (x, y) => x.Episodes = y,
(x, y) => { x.Season = y; x.SeasonID = y.ID; }), (x, y) => { x.Season = y; x.SeasonID = y.ID; }),
(Season s, nameof(Season.Show)) => ShowRepository (Season s, nameof(Season.Show)) => ShowRepository
.GetOrDefault(x => x.Seasons.Any(y => y.ID == obj.ID)) .GetOrDefault(x => x.Seasons.Any(y => y.ID == obj.ID))
.Then(x => .Then(x =>
@ -311,18 +311,18 @@ namespace Kyoo.Core.Controllers
s.Show = x; s.Show = x;
s.ShowID = x?.ID ?? 0; s.ShowID = x?.ID ?? 0;
}), }),
(Episode e, nameof(Episode.ExternalIDs)) => SetRelation(e, (Episode e, nameof(Episode.ExternalIDs)) => SetRelation(e,
ProviderRepository.GetMetadataID<Episode>(x => x.ResourceID == obj.ID), ProviderRepository.GetMetadataID<Episode>(x => x.ResourceID == obj.ID),
(x, y) => x.ExternalIDs = y, (x, y) => x.ExternalIDs = y,
(x, y) => { x.ResourceID = y.ID; }), (x, y) => { x.ResourceID = y.ID; }),
(Episode e, nameof(Episode.Tracks)) => SetRelation(e, (Episode e, nameof(Episode.Tracks)) => SetRelation(e,
TrackRepository.GetAll(x => x.Episode.ID == obj.ID), TrackRepository.GetAll(x => x.Episode.ID == obj.ID),
(x, y) => x.Tracks = y, (x, y) => x.Tracks = y,
(x, y) => { x.Episode = y; x.EpisodeID = y.ID; }), (x, y) => { x.Episode = y; x.EpisodeID = y.ID; }),
(Episode e, nameof(Episode.Show)) => ShowRepository (Episode e, nameof(Episode.Show)) => ShowRepository
.GetOrDefault(x => x.Episodes.Any(y => y.ID == obj.ID)) .GetOrDefault(x => x.Episodes.Any(y => y.ID == obj.ID))
.Then(x => .Then(x =>
@ -330,7 +330,7 @@ namespace Kyoo.Core.Controllers
e.Show = x; e.Show = x;
e.ShowID = x?.ID ?? 0; e.ShowID = x?.ID ?? 0;
}), }),
(Episode e, nameof(Episode.Season)) => SeasonRepository (Episode e, nameof(Episode.Season)) => SeasonRepository
.GetOrDefault(x => x.Episodes.Any(y => y.ID == e.ID)) .GetOrDefault(x => x.Episodes.Any(y => y.ID == e.ID))
.Then(x => .Then(x =>
@ -338,8 +338,8 @@ namespace Kyoo.Core.Controllers
e.Season = x; e.Season = x;
e.SeasonID = x?.ID ?? 0; e.SeasonID = x?.ID ?? 0;
}), }),
(Track t, nameof(Track.Episode)) => EpisodeRepository (Track t, nameof(Track.Episode)) => EpisodeRepository
.GetOrDefault(x => x.Tracks.Any(y => y.ID == obj.ID)) .GetOrDefault(x => x.Tracks.Any(y => y.ID == obj.ID))
.Then(x => .Then(x =>
@ -347,62 +347,62 @@ namespace Kyoo.Core.Controllers
t.Episode = x; t.Episode = x;
t.EpisodeID = x?.ID ?? 0; t.EpisodeID = x?.ID ?? 0;
}), }),
(Genre g, nameof(Genre.Shows)) => ShowRepository (Genre g, nameof(Genre.Shows)) => ShowRepository
.GetAll(x => x.Genres.Any(y => y.ID == obj.ID)) .GetAll(x => x.Genres.Any(y => y.ID == obj.ID))
.Then(x => g.Shows = x), .Then(x => g.Shows = x),
(Studio s, nameof(Studio.Shows)) => ShowRepository (Studio s, nameof(Studio.Shows)) => ShowRepository
.GetAll(x => x.Studio.ID == obj.ID) .GetAll(x => x.Studio.ID == obj.ID)
.Then(x => s.Shows = x), .Then(x => s.Shows = x),
(Studio s, nameof(Studio.ExternalIDs)) => SetRelation(s, (Studio s, nameof(Studio.ExternalIDs)) => SetRelation(s,
ProviderRepository.GetMetadataID<Studio>(x => x.ResourceID == obj.ID), ProviderRepository.GetMetadataID<Studio>(x => x.ResourceID == obj.ID),
(x, y) => x.ExternalIDs = y, (x, y) => x.ExternalIDs = y,
(x, y) => { x.ResourceID = y.ID; }), (x, y) => { x.ResourceID = y.ID; }),
(People p, nameof(People.ExternalIDs)) => SetRelation(p, (People p, nameof(People.ExternalIDs)) => SetRelation(p,
ProviderRepository.GetMetadataID<People>(x => x.ResourceID == obj.ID), ProviderRepository.GetMetadataID<People>(x => x.ResourceID == obj.ID),
(x, y) => x.ExternalIDs = y, (x, y) => x.ExternalIDs = y,
(x, y) => { x.ResourceID = y.ID; }), (x, y) => { x.ResourceID = y.ID; }),
(People p, nameof(People.Roles)) => PeopleRepository (People p, nameof(People.Roles)) => PeopleRepository
.GetFromPeople(obj.ID) .GetFromPeople(obj.ID)
.Then(x => p.Roles = x), .Then(x => p.Roles = x),
(Provider p, nameof(Provider.Libraries)) => LibraryRepository (Provider p, nameof(Provider.Libraries)) => LibraryRepository
.GetAll(x => x.Providers.Any(y => y.ID == obj.ID)) .GetAll(x => x.Providers.Any(y => y.ID == obj.ID))
.Then(x => p.Libraries = x), .Then(x => p.Libraries = x),
_ => throw new ArgumentException($"Couldn't find a way to load {memberName} of {obj.Slug}.") _ => throw new ArgumentException($"Couldn't find a way to load {memberName} of {obj.Slug}.")
}; };
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id, public Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return LibraryItemRepository.GetFromLibrary(id, where, sort, limit); return LibraryItemRepository.GetFromLibrary(id, where, sort, limit);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug, public Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug,
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return LibraryItemRepository.GetFromLibrary(slug, where, sort, limit); return LibraryItemRepository.GetFromLibrary(slug, where, sort, limit);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID, public Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
@ -411,16 +411,16 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug, public Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return PeopleRepository.GetFromShow(showSlug, where, sort, limit); return PeopleRepository.GetFromShow(showSlug, where, sort, limit);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<PeopleRole>> GetRolesFromPeople(int id, public Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
@ -454,7 +454,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null, public Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
Pagination limit = default) Pagination limit = default)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().GetAll(where, sort, limit); return GetRepository<T>().GetAll(where, sort, limit);
@ -468,19 +468,19 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<ICollection<T>> Search<T>(string query) public Task<ICollection<T>> Search<T>(string query)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().Search(query); return GetRepository<T>().Search(query);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<T> Create<T>(T item) public Task<T> Create<T>(T item)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().Create(item); return GetRepository<T>().Create(item);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<T> CreateIfNotExists<T>(T item) public Task<T> CreateIfNotExists<T>(T item)
where T : class, IResource where T : class, IResource
@ -496,21 +496,21 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public Task Delete<T>(T item) public Task Delete<T>(T item)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().Delete(item); return GetRepository<T>().Delete(item);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task Delete<T>(int id) public Task Delete<T>(int id)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().Delete(id); return GetRepository<T>().Delete(id);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task Delete<T>(string slug) public Task Delete<T>(string slug)
where T : class, IResource where T : class, IResource
{ {
return GetRepository<T>().Delete(slug); return GetRepository<T>().Delete(slug);

View File

@ -14,7 +14,7 @@ namespace Kyoo.Core.Controllers
{ {
logger.LogWarning("No permission validator has been enabled, all users will have all permissions"); logger.LogWarning("No permission validator has been enabled, all users will have all permissions");
} }
/// <inheritdoc /> /// <inheritdoc />
public IFilterMetadata Create(PermissionAttribute attribute) public IFilterMetadata Create(PermissionAttribute attribute)
{ {

View File

@ -30,7 +30,7 @@ namespace Kyoo.Core.Controllers
/// The logger used by this class. /// The logger used by this class.
/// </summary> /// </summary>
private readonly ILogger<PluginManager> _logger; private readonly ILogger<PluginManager> _logger;
/// <summary> /// <summary>
/// The list of plugins that are currently loaded. /// The list of plugins that are currently loaded.
/// </summary> /// </summary>
@ -93,7 +93,7 @@ namespace Kyoo.Core.Controllers
return Array.Empty<IPlugin>(); return Array.Empty<IPlugin>();
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public void LoadPlugins(ICollection<IPlugin> plugins) public void LoadPlugins(ICollection<IPlugin> plugins)
{ {

View File

@ -40,7 +40,7 @@ namespace Kyoo.Core.Controllers
_providers = providers.ToArray(); _providers = providers.ToArray();
_logger = logger; _logger = logger;
} }
/// <inheritdoc /> /// <inheritdoc />
public override void UseProviders(IEnumerable<Provider> providers) public override void UseProviders(IEnumerable<Provider> providers)
@ -71,9 +71,9 @@ namespace Kyoo.Core.Controllers
{ {
ret = Merger.Merge(ret, await provider.Get(ret)); ret = Merger.Merge(ret, await provider.Get(ret));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "The provider {Provider} could not get a {Type}", _logger.LogError(ex, "The provider {Provider} could not get a {Type}",
provider.Provider.Name, typeof(T).Name); provider.Provider.Name, typeof(T).Name);
} }
} }
@ -85,16 +85,16 @@ namespace Kyoo.Core.Controllers
public override async Task<ICollection<T>> Search<T>(string query) public override async Task<ICollection<T>> Search<T>(string query)
{ {
List<T> ret = new(); List<T> ret = new();
foreach (IMetadataProvider provider in _GetProviders()) foreach (IMetadataProvider provider in _GetProviders())
{ {
try try
{ {
ret.AddRange(await provider.Search<T>(query)); ret.AddRange(await provider.Search<T>(query));
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "The provider {Provider} could not search for {Type}", _logger.LogError(ex, "The provider {Provider} could not search for {Type}",
provider.Provider.Name, typeof(T).Name); provider.Provider.Name, typeof(T).Name);
} }
} }

View File

@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
.FirstOrDefault(); .FirstOrDefault();
return path[(libraryPath?.Length ?? 0)..]; return path[(libraryPath?.Length ?? 0)..];
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<(Collection, Show, Season, Episode)> Identify(string path) public async Task<(Collection, Show, Season, Episode)> Identify(string path)
{ {
@ -77,21 +77,21 @@ namespace Kyoo.Core.Controllers
Slug = Utility.ToSlug(match.Groups["Show"].Value), Slug = Utility.ToSlug(match.Groups["Show"].Value),
Title = match.Groups["Show"].Value, Title = match.Groups["Show"].Value,
Path = Path.GetDirectoryName(path), Path = Path.GetDirectoryName(path),
StartAir = match.Groups["StartYear"].Success StartAir = match.Groups["StartYear"].Success
? new DateTime(int.Parse(match.Groups["StartYear"].Value), 1, 1) ? new DateTime(int.Parse(match.Groups["StartYear"].Value), 1, 1)
: null : null
}, },
season: null, season: null,
episode: new Episode episode: new Episode
{ {
SeasonNumber = match.Groups["Season"].Success SeasonNumber = match.Groups["Season"].Success
? int.Parse(match.Groups["Season"].Value) ? int.Parse(match.Groups["Season"].Value)
: null, : null,
EpisodeNumber = match.Groups["Episode"].Success EpisodeNumber = match.Groups["Episode"].Success
? int.Parse(match.Groups["Episode"].Value) ? int.Parse(match.Groups["Episode"].Value)
: null, : null,
AbsoluteNumber = match.Groups["Absolute"].Success AbsoluteNumber = match.Groups["Absolute"].Success
? int.Parse(match.Groups["Absolute"].Value) ? int.Parse(match.Groups["Absolute"].Value)
: null, : null,
Path = path Path = path
} }

View File

@ -19,12 +19,12 @@ namespace Kyoo.Core.Controllers
/// The database handle /// The database handle
/// </summary> /// </summary>
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
/// <summary> /// <summary>
/// A provider repository to handle externalID creation and deletion /// A provider repository to handle externalID creation and deletion
/// </summary> /// </summary>
private readonly IProviderRepository _providers; private readonly IProviderRepository _providers;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<Collection, object>> DefaultSort => x => x.Name; protected override Expression<Func<Collection, object>> DefaultSort => x => x.Name;
@ -58,12 +58,12 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync($"Trying to insert a duplicated collection (slug {obj.Slug} already exists)."); await _database.SaveChangesAsync($"Trying to insert a duplicated collection (slug {obj.Slug} already exists).");
return obj; return obj;
} }
/// <inheritdoc /> /// <inheritdoc />
protected override async Task Validate(Collection resource) protected override async Task Validate(Collection resource)
{ {
await base.Validate(resource); await base.Validate(resource);
if (string.IsNullOrEmpty(resource.Slug)) if (string.IsNullOrEmpty(resource.Slug))
throw new ArgumentException("The collection's slug must be set and not empty"); throw new ArgumentException("The collection's slug must be set and not empty");
if (string.IsNullOrEmpty(resource.Name)) if (string.IsNullOrEmpty(resource.Name))
@ -72,7 +72,7 @@ namespace Kyoo.Core.Controllers
if (resource.ExternalIDs != null) if (resource.ExternalIDs != null)
{ {
foreach (MetadataID id in resource.ExternalIDs) foreach (MetadataID id in resource.ExternalIDs)
{ {
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug) id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
?? await _providers.CreateIfNotExists(id.Provider); ?? await _providers.CreateIfNotExists(id.Provider);
id.ProviderID = id.Provider.ID; id.ProviderID = id.Provider.ID;
@ -80,12 +80,12 @@ namespace Kyoo.Core.Controllers
_database.MetadataIds<Collection>().AttachRange(resource.ExternalIDs); _database.MetadataIds<Collection>().AttachRange(resource.ExternalIDs);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override async Task EditRelations(Collection resource, Collection changed, bool resetOld) protected override async Task EditRelations(Collection resource, Collection changed, bool resetOld)
{ {
await Validate(changed); await Validate(changed);
if (changed.ExternalIDs != null || resetOld) if (changed.ExternalIDs != null || resetOld)
{ {
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync(); await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();

View File

@ -29,7 +29,7 @@ namespace Kyoo.Core.Controllers
/// A track repository to handle creation and deletion of tracks related to the current episode. /// A track repository to handle creation and deletion of tracks related to the current episode.
/// </summary> /// </summary>
private readonly ITrackRepository _tracks; private readonly ITrackRepository _tracks;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<Episode, object>> DefaultSort => x => x.EpisodeNumber; protected override Expression<Func<Episode, object>> DefaultSort => x => x.EpisodeNumber;
@ -42,27 +42,27 @@ namespace Kyoo.Core.Controllers
/// <param name="tracks">A track repository</param> /// <param name="tracks">A track repository</param>
public EpisodeRepository(DatabaseContext database, public EpisodeRepository(DatabaseContext database,
IProviderRepository providers, IProviderRepository providers,
ITrackRepository tracks) ITrackRepository tracks)
: base(database) : base(database)
{ {
_database = database; _database = database;
_providers = providers; _providers = providers;
_tracks = tracks; _tracks = tracks;
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber) public Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
{ {
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
&& x.SeasonNumber == seasonNumber && x.SeasonNumber == seasonNumber
&& x.EpisodeNumber == episodeNumber); && x.EpisodeNumber == episodeNumber);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber) public Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
{ {
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber && x.SeasonNumber == seasonNumber
&& x.EpisodeNumber == episodeNumber); && x.EpisodeNumber == episodeNumber);
} }
@ -87,14 +87,14 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc /> /// <inheritdoc />
public Task<Episode> GetAbsolute(int showID, int absoluteNumber) public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
{ {
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
&& x.AbsoluteNumber == absoluteNumber); && x.AbsoluteNumber == absoluteNumber);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<Episode> GetAbsolute(string showSlug, int absoluteNumber) public Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
{ {
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.AbsoluteNumber == absoluteNumber); && x.AbsoluteNumber == absoluteNumber);
} }
@ -108,7 +108,7 @@ namespace Kyoo.Core.Controllers
.Take(20) .Take(20)
.ToListAsync(); .ToListAsync();
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<Episode> Create(Episode obj) public override async Task<Episode> Create(Episode obj)
{ {
@ -146,7 +146,7 @@ namespace Kyoo.Core.Controllers
{ {
if (resource.Tracks == null) if (resource.Tracks == null)
return resource; return resource;
resource.Tracks = await resource.Tracks.SelectAsync(x => resource.Tracks = await resource.Tracks.SelectAsync(x =>
{ {
x.Episode = resource; x.Episode = resource;
@ -156,7 +156,7 @@ namespace Kyoo.Core.Controllers
_database.Tracks.AttachRange(resource.Tracks); _database.Tracks.AttachRange(resource.Tracks);
return resource; return resource;
} }
/// <inheritdoc /> /// <inheritdoc />
protected override async Task Validate(Episode resource) protected override async Task Validate(Episode resource)
{ {
@ -180,17 +180,17 @@ namespace Kyoo.Core.Controllers
_database.MetadataIds<Episode>().AttachRange(resource.ExternalIDs); _database.MetadataIds<Episode>().AttachRange(resource.ExternalIDs);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task Delete(Episode obj) public override async Task Delete(Episode obj)
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
await obj.Tracks.ForEachAsync(x => _tracks.Delete(x)); await obj.Tracks.ForEachAsync(x => _tracks.Delete(x));
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted); obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
} }
} }
} }

View File

@ -19,11 +19,11 @@ namespace Kyoo.Core.Controllers
/// The database handle /// The database handle
/// </summary> /// </summary>
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<Genre, object>> DefaultSort => x => x.Slug; protected override Expression<Func<Genre, object>> DefaultSort => x => x.Slug;
/// <summary> /// <summary>
/// Create a new <see cref="GenreRepository"/>. /// Create a new <see cref="GenreRepository"/>.
/// </summary> /// </summary>

View File

@ -34,7 +34,7 @@ namespace Kyoo.Core.Controllers
/// </summary> /// </summary>
/// <param name="database">The database instance</param> /// <param name="database">The database instance</param>
/// <param name="libraries">A lazy loaded library repository</param> /// <param name="libraries">A lazy loaded library repository</param>
public LibraryItemRepository(DatabaseContext database, public LibraryItemRepository(DatabaseContext database,
Lazy<ILibraryRepository> libraries) Lazy<ILibraryRepository> libraries)
: base(database) : base(database)
{ {
@ -42,13 +42,13 @@ namespace Kyoo.Core.Controllers
_libraries = libraries; _libraries = libraries;
} }
/// <inheritdoc /> /// <inheritdoc />
public override Task<LibraryItem> GetOrDefault(int id) public override Task<LibraryItem> GetOrDefault(int id)
{ {
return _database.LibraryItems.FirstOrDefaultAsync(x => x.ID == id); return _database.LibraryItems.FirstOrDefaultAsync(x => x.ID == id);
} }
/// <inheritdoc /> /// <inheritdoc />
public override Task<LibraryItem> GetOrDefault(string slug) public override Task<LibraryItem> GetOrDefault(string slug)
{ {
@ -83,27 +83,27 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public override Task<LibraryItem> Create(LibraryItem obj) public override Task<LibraryItem> Create(LibraryItem obj)
=> throw new InvalidOperationException(); => throw new InvalidOperationException();
/// <inheritdoc /> /// <inheritdoc />
public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj) public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj)
=> throw new InvalidOperationException(); => throw new InvalidOperationException();
/// <inheritdoc /> /// <inheritdoc />
public override Task<LibraryItem> Edit(LibraryItem obj, bool resetOld) public override Task<LibraryItem> Edit(LibraryItem obj, bool resetOld)
=> throw new InvalidOperationException(); => throw new InvalidOperationException();
/// <inheritdoc /> /// <inheritdoc />
public override Task Delete(int id) public override Task Delete(int id)
=> throw new InvalidOperationException(); => throw new InvalidOperationException();
/// <inheritdoc /> /// <inheritdoc />
public override Task Delete(string slug) public override Task Delete(string slug)
=> throw new InvalidOperationException(); => throw new InvalidOperationException();
/// <inheritdoc /> /// <inheritdoc />
public override Task Delete(LibraryItem obj) public override Task Delete(LibraryItem obj)
=> throw new InvalidOperationException(); => throw new InvalidOperationException();
/// <summary> /// <summary>
@ -125,8 +125,8 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc /> /// <inheritdoc />
public async Task<ICollection<LibraryItem>> GetFromLibrary(int id, public async Task<ICollection<LibraryItem>> GetFromLibrary(int id,
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.ID == id), ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.ID == id),
@ -137,11 +137,11 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException(); throw new ItemNotFoundException();
return items; return items;
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<ICollection<LibraryItem>> GetFromLibrary(string slug, public async Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
Expression<Func<LibraryItem, bool>> where = null, Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default, Sort<LibraryItem> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.Slug == slug), ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.Slug == slug),

View File

@ -24,7 +24,7 @@ namespace Kyoo.Core.Controllers
/// A provider repository to handle externalID creation and deletion /// A provider repository to handle externalID creation and deletion
/// </summary> /// </summary>
private readonly IProviderRepository _providers; private readonly IProviderRepository _providers;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<Library, object>> DefaultSort => x => x.ID; protected override Expression<Func<Library, object>> DefaultSort => x => x.ID;
@ -41,7 +41,7 @@ namespace Kyoo.Core.Controllers
_providers = providers; _providers = providers;
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ICollection<Library>> Search(string query) public override async Task<ICollection<Library>> Search(string query)
{ {
@ -65,14 +65,14 @@ namespace Kyoo.Core.Controllers
protected override async Task Validate(Library resource) protected override async Task Validate(Library resource)
{ {
await base.Validate(resource); await base.Validate(resource);
if (string.IsNullOrEmpty(resource.Slug)) if (string.IsNullOrEmpty(resource.Slug))
throw new ArgumentException("The library's slug must be set and not empty"); throw new ArgumentException("The library's slug must be set and not empty");
if (string.IsNullOrEmpty(resource.Name)) if (string.IsNullOrEmpty(resource.Name))
throw new ArgumentException("The library's name must be set and not empty"); throw new ArgumentException("The library's name must be set and not empty");
if (resource.Paths == null || !resource.Paths.Any()) if (resource.Paths == null || !resource.Paths.Any())
throw new ArgumentException("The library should have a least one path."); throw new ArgumentException("The library should have a least one path.");
if (resource.Providers != null) if (resource.Providers != null)
{ {
resource.Providers = await resource.Providers resource.Providers = await resource.Providers
@ -99,7 +99,7 @@ namespace Kyoo.Core.Controllers
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
} }

View File

@ -30,8 +30,8 @@ namespace Kyoo.Core.Controllers
/// The default sort order that will be used for this resource's type. /// The default sort order that will be used for this resource's type.
/// </summary> /// </summary>
protected abstract Expression<Func<T, object>> DefaultSort { get; } protected abstract Expression<Func<T, object>> DefaultSort { get; }
/// <summary> /// <summary>
/// Create a new base <see cref="LocalRepository{T}"/> with the given database handle. /// Create a new base <see cref="LocalRepository{T}"/> with the given database handle.
/// </summary> /// </summary>
@ -57,7 +57,7 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}"); throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}");
return ret; return ret;
} }
/// <inheritdoc/> /// <inheritdoc/>
public virtual async Task<T> Get(int id) public virtual async Task<T> Get(int id)
{ {
@ -84,19 +84,19 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate."); throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
return ret; return ret;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual Task<T> GetOrDefault(int id) public virtual Task<T> GetOrDefault(int id)
{ {
return Database.Set<T>().FirstOrDefaultAsync(x => x.ID == id); return Database.Set<T>().FirstOrDefaultAsync(x => x.ID == id);
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual Task<T> GetOrDefault(string slug) public virtual Task<T> GetOrDefault(string slug)
{ {
return Database.Set<T>().FirstOrDefaultAsync(x => x.Slug == slug); return Database.Set<T>().FirstOrDefaultAsync(x => x.Slug == slug);
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual Task<T> GetOrDefault(Expression<Func<T, bool>> where) public virtual Task<T> GetOrDefault(Expression<Func<T, bool>> where)
{ {
@ -105,7 +105,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc/> /// <inheritdoc/>
public abstract Task<ICollection<T>> Search(string query); public abstract Task<ICollection<T>> Search(string query);
/// <inheritdoc/> /// <inheritdoc/>
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null, public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
@ -113,7 +113,7 @@ namespace Kyoo.Core.Controllers
{ {
return ApplyFilters(Database.Set<T>(), where, sort, limit); return ApplyFilters(Database.Set<T>(), where, sort, limit);
} }
/// <summary> /// <summary>
/// Apply filters to a query to ease sort, pagination & where queries for resources of this repository /// Apply filters to a query to ease sort, pagination & where queries for resources of this repository
/// </summary> /// </summary>
@ -124,12 +124,12 @@ namespace Kyoo.Core.Controllers
/// <returns>The filtered query</returns> /// <returns>The filtered query</returns>
protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query, protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return ApplyFilters(query, GetOrDefault, DefaultSort, where, sort, limit); return ApplyFilters(query, GetOrDefault, DefaultSort, where, sort, limit);
} }
/// <summary> /// <summary>
/// Apply filters to a query to ease sort, pagination & where queries for any resources types. /// Apply filters to a query to ease sort, pagination & where queries for any resources types.
/// For resources of type <see cref="T"/>, see <see cref="ApplyFilters"/> /// For resources of type <see cref="T"/>, see <see cref="ApplyFilters"/>
@ -145,17 +145,17 @@ namespace Kyoo.Core.Controllers
Func<int, Task<TValue>> get, Func<int, Task<TValue>> get,
Expression<Func<TValue, object>> defaultSort, Expression<Func<TValue, object>> defaultSort,
Expression<Func<TValue, bool>> where = null, Expression<Func<TValue, bool>> where = null,
Sort<TValue> sort = default, Sort<TValue> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
if (where != null) if (where != null)
query = query.Where(where); query = query.Where(where);
Expression<Func<TValue, object>> sortKey = sort.Key ?? defaultSort; Expression<Func<TValue, object>> sortKey = sort.Key ?? defaultSort;
Expression sortExpression = sortKey.Body.NodeType == ExpressionType.Convert Expression sortExpression = sortKey.Body.NodeType == ExpressionType.Convert
? ((UnaryExpression)sortKey.Body).Operand ? ((UnaryExpression)sortKey.Body).Operand
: sortKey.Body; : sortKey.Body;
if (typeof(Enum).IsAssignableFrom(sortExpression.Type)) if (typeof(Enum).IsAssignableFrom(sortExpression.Type))
throw new ArgumentException("Invalid sort key."); throw new ArgumentException("Invalid sort key.");
@ -205,7 +205,7 @@ namespace Kyoo.Core.Controllers
T old = await GetOrDefault(obj.Slug); T old = await GetOrDefault(obj.Slug);
if (old != null) if (old != null)
return old; return old;
return await Create(obj); return await Create(obj);
} }
catch (DuplicatedItemException) catch (DuplicatedItemException)
@ -225,7 +225,7 @@ namespace Kyoo.Core.Controllers
try try
{ {
T old = await GetWithTracking(edited.ID); T old = await GetWithTracking(edited.ID);
if (resetOld) if (resetOld)
old = Merger.Nullify(old); old = Merger.Nullify(old);
Merger.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null); Merger.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
@ -239,7 +239,7 @@ namespace Kyoo.Core.Controllers
Database.ChangeTracker.Clear(); Database.ChangeTracker.Clear();
} }
} }
/// <summary> /// <summary>
/// An overridable method to edit relation of a resource. /// An overridable method to edit relation of a resource.
/// </summary> /// </summary>
@ -257,7 +257,7 @@ namespace Kyoo.Core.Controllers
{ {
return Validate(resource); return Validate(resource);
} }
/// <summary> /// <summary>
/// A method called just before saving a new resource to the database. /// A method called just before saving a new resource to the database.
/// It is also called on the default implementation of <see cref="EditRelations"/> /// It is also called on the default implementation of <see cref="EditRelations"/>
@ -278,7 +278,7 @@ namespace Kyoo.Core.Controllers
{ {
MethodInfo setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod(); MethodInfo setter = typeof(T).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
throw new ArgumentException("Resources slug can't be number only."); throw new ArgumentException("Resources slug can't be number only.");
} }
@ -306,7 +306,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc/> /// <inheritdoc/>
public abstract Task Delete(T obj); public abstract Task Delete(T obj);
/// <inheritdoc/> /// <inheritdoc/>
public async Task DeleteAll(Expression<Func<T, bool>> where) public async Task DeleteAll(Expression<Func<T, bool>> where)
{ {

View File

@ -29,7 +29,7 @@ namespace Kyoo.Core.Controllers
/// A lazy loaded show repository to validate requests from shows. /// A lazy loaded show repository to validate requests from shows.
/// </summary> /// </summary>
private readonly Lazy<IShowRepository> _shows; private readonly Lazy<IShowRepository> _shows;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<People, object>> DefaultSort => x => x.Name; protected override Expression<Func<People, object>> DefaultSort => x => x.Name;
@ -41,14 +41,14 @@ namespace Kyoo.Core.Controllers
/// <param name="shows">A lazy loaded show repository</param> /// <param name="shows">A lazy loaded show repository</param>
public PeopleRepository(DatabaseContext database, public PeopleRepository(DatabaseContext database,
IProviderRepository providers, IProviderRepository providers,
Lazy<IShowRepository> shows) Lazy<IShowRepository> shows)
: base(database) : base(database)
{ {
_database = database; _database = database;
_providers = providers; _providers = providers;
_shows = shows; _shows = shows;
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ICollection<People>> Search(string query) public override async Task<ICollection<People>> Search(string query)
@ -89,7 +89,7 @@ namespace Kyoo.Core.Controllers
{ {
foreach (PeopleRole role in resource.Roles) foreach (PeopleRole role in resource.Roles)
{ {
role.Show = _database.LocalEntity<Show>(role.Show.Slug) role.Show = _database.LocalEntity<Show>(role.Show.Slug)
?? await _shows.Value.CreateIfNotExists(role.Show); ?? await _shows.Value.CreateIfNotExists(role.Show);
role.ShowID = role.Show.ID; role.ShowID = role.Show.ID;
_database.Entry(role).State = EntityState.Added; _database.Entry(role).State = EntityState.Added;
@ -101,7 +101,7 @@ namespace Kyoo.Core.Controllers
protected override async Task EditRelations(People resource, People changed, bool resetOld) protected override async Task EditRelations(People resource, People changed, bool resetOld)
{ {
await Validate(changed); await Validate(changed);
if (changed.Roles != null || resetOld) if (changed.Roles != null || resetOld)
{ {
await Database.Entry(resource).Collection(x => x.Roles).LoadAsync(); await Database.Entry(resource).Collection(x => x.Roles).LoadAsync();
@ -120,7 +120,7 @@ namespace Kyoo.Core.Controllers
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted); obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
obj.Roles.ForEach(x => _database.Entry(x).State = EntityState.Deleted); obj.Roles.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
@ -128,9 +128,9 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromShow(int showID, public async Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
@ -151,7 +151,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc /> /// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromShow(string showSlug, public async Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
@ -169,11 +169,11 @@ namespace Kyoo.Core.Controllers
role.ForPeople = true; role.ForPeople = true;
return people; return people;
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromPeople(int id, public async Task<ICollection<PeopleRole>> GetFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
@ -188,11 +188,11 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException(); throw new ItemNotFoundException();
return roles; return roles;
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromPeople(string slug, public async Task<ICollection<PeopleRole>> GetFromPeople(string slug,
Expression<Func<PeopleRole, bool>> where = null, Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default, Sort<PeopleRole> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles

View File

@ -50,7 +50,7 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showID}"); throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showID}");
return ret; return ret;
} }
/// <inheritdoc/> /// <inheritdoc/>
public async Task<Season> Get(string showSlug, int seasonNumber) public async Task<Season> Get(string showSlug, int seasonNumber)
{ {
@ -63,14 +63,14 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc/> /// <inheritdoc/>
public Task<Season> GetOrDefault(int showID, int seasonNumber) public Task<Season> GetOrDefault(int showID, int seasonNumber)
{ {
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
&& x.SeasonNumber == seasonNumber); && x.SeasonNumber == seasonNumber);
} }
/// <inheritdoc/> /// <inheritdoc/>
public Task<Season> GetOrDefault(string showSlug, int seasonNumber) public Task<Season> GetOrDefault(string showSlug, int seasonNumber)
{ {
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber); && x.SeasonNumber == seasonNumber);
} }
@ -121,14 +121,14 @@ namespace Kyoo.Core.Controllers
protected override async Task EditRelations(Season resource, Season changed, bool resetOld) protected override async Task EditRelations(Season resource, Season changed, bool resetOld)
{ {
await Validate(changed); await Validate(changed);
if (changed.ExternalIDs != null || resetOld) if (changed.ExternalIDs != null || resetOld)
{ {
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync(); await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
resource.ExternalIDs = changed.ExternalIDs; resource.ExternalIDs = changed.ExternalIDs;
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
public override async Task Delete(Season obj) public override async Task Delete(Season obj)
{ {
@ -139,4 +139,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
} }
} }
} }

View File

@ -50,8 +50,8 @@ namespace Kyoo.Core.Controllers
/// <param name="providers">A provider repository</param> /// <param name="providers">A provider repository</param>
public ShowRepository(DatabaseContext database, public ShowRepository(DatabaseContext database,
IStudioRepository studios, IStudioRepository studios,
IPeopleRepository people, IPeopleRepository people,
IGenreRepository genres, IGenreRepository genres,
IProviderRepository providers) IProviderRepository providers)
: base(database) : base(database)
{ {
@ -61,7 +61,7 @@ namespace Kyoo.Core.Controllers
_genres = genres; _genres = genres;
_providers = providers; _providers = providers;
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ICollection<Show>> Search(string query) public override async Task<ICollection<Show>> Search(string query)
@ -82,7 +82,7 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync($"Trying to insert a duplicated show (slug {obj.Slug} already exists)."); await _database.SaveChangesAsync($"Trying to insert a duplicated show (slug {obj.Slug} already exists).");
return obj; return obj;
} }
/// <inheritdoc /> /// <inheritdoc />
protected override async Task Validate(Show resource) protected override async Task Validate(Show resource)
{ {
@ -128,7 +128,7 @@ namespace Kyoo.Core.Controllers
protected override async Task EditRelations(Show resource, Show changed, bool resetOld) protected override async Task EditRelations(Show resource, Show changed, bool resetOld)
{ {
await Validate(changed); await Validate(changed);
if (changed.Aliases != null || resetOld) if (changed.Aliases != null || resetOld)
resource.Aliases = changed.Aliases; resource.Aliases = changed.Aliases;
@ -137,7 +137,7 @@ namespace Kyoo.Core.Controllers
await Database.Entry(resource).Reference(x => x.Studio).LoadAsync(); await Database.Entry(resource).Reference(x => x.Studio).LoadAsync();
resource.Studio = changed.Studio; resource.Studio = changed.Studio;
} }
if (changed.Genres != null || resetOld) if (changed.Genres != null || resetOld)
{ {
await Database.Entry(resource).Collection(x => x.Genres).LoadAsync(); await Database.Entry(resource).Collection(x => x.Genres).LoadAsync();
@ -177,7 +177,7 @@ namespace Kyoo.Core.Controllers
await _database.SaveIfNoDuplicates(); await _database.SaveIfNoDuplicates();
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<string> GetSlug(int showID) public Task<string> GetSlug(int showID)
{ {
@ -185,7 +185,7 @@ namespace Kyoo.Core.Controllers
.Select(x => x.Slug) .Select(x => x.Slug)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task Delete(Show obj) public override async Task Delete(Show obj)
{ {

View File

@ -19,12 +19,12 @@ namespace Kyoo.Core.Controllers
/// The database handle /// The database handle
/// </summary> /// </summary>
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
/// <summary> /// <summary>
/// A provider repository to handle externalID creation and deletion /// A provider repository to handle externalID creation and deletion
/// </summary> /// </summary>
private readonly IProviderRepository _providers; private readonly IProviderRepository _providers;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<Studio, object>> DefaultSort => x => x.Name; protected override Expression<Func<Studio, object>> DefaultSort => x => x.Name;
@ -40,7 +40,7 @@ namespace Kyoo.Core.Controllers
_database = database; _database = database;
_providers = providers; _providers = providers;
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ICollection<Studio>> Search(string query) public override async Task<ICollection<Studio>> Search(string query)
{ {
@ -59,7 +59,7 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync($"Trying to insert a duplicated studio (slug {obj.Slug} already exists)."); await _database.SaveChangesAsync($"Trying to insert a duplicated studio (slug {obj.Slug} already exists).");
return obj; return obj;
} }
/// <inheritdoc /> /// <inheritdoc />
protected override async Task Validate(Studio resource) protected override async Task Validate(Studio resource)
{ {
@ -75,7 +75,7 @@ namespace Kyoo.Core.Controllers
_database.MetadataIds<Studio>().AttachRange(resource.ExternalIDs); _database.MetadataIds<Studio>().AttachRange(resource.ExternalIDs);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override async Task EditRelations(Studio resource, Studio changed, bool resetOld) protected override async Task EditRelations(Studio resource, Studio changed, bool resetOld)
{ {
@ -93,7 +93,7 @@ namespace Kyoo.Core.Controllers
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
} }

View File

@ -18,7 +18,7 @@ namespace Kyoo.Core.Controllers
/// The database handle /// The database handle
/// </summary> /// </summary>
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<Track, object>> DefaultSort => x => x.TrackIndex; protected override Expression<Func<Track, object>> DefaultSort => x => x.TrackIndex;
@ -27,7 +27,7 @@ namespace Kyoo.Core.Controllers
/// Create a new <see cref="TrackRepository"/>. /// Create a new <see cref="TrackRepository"/>.
/// </summary> /// </summary>
/// <param name="database">The database handle</param> /// <param name="database">The database handle</param>
public TrackRepository(DatabaseContext database) public TrackRepository(DatabaseContext database)
: base(database) : base(database)
{ {
_database = database; _database = database;
@ -69,7 +69,7 @@ namespace Kyoo.Core.Controllers
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
} }

View File

@ -19,11 +19,11 @@ namespace Kyoo.Core.Controllers
/// The database handle /// The database handle
/// </summary> /// </summary>
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
/// <inheritdoc /> /// <inheritdoc />
protected override Expression<Func<User, object>> DefaultSort => x => x.Username; protected override Expression<Func<User, object>> DefaultSort => x => x.Username;
/// <summary> /// <summary>
/// Create a new <see cref="UserRepository"/> /// Create a new <see cref="UserRepository"/>
/// </summary> /// </summary>
@ -33,7 +33,7 @@ namespace Kyoo.Core.Controllers
{ {
_database = database; _database = database;
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<ICollection<User>> Search(string query) public override async Task<ICollection<User>> Search(string query)
{ {
@ -43,7 +43,7 @@ namespace Kyoo.Core.Controllers
.Take(20) .Take(20)
.ToListAsync(); .ToListAsync();
} }
/// <inheritdoc /> /// <inheritdoc />
public override async Task<User> Create(User obj) public override async Task<User> Create(User obj)
{ {
@ -58,7 +58,7 @@ namespace Kyoo.Core.Controllers
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
} }

View File

@ -32,7 +32,7 @@ namespace Kyoo.Core.Controllers
/// The metadata for this task (the slug, and other useful information). /// The metadata for this task (the slug, and other useful information).
/// </summary> /// </summary>
public TaskMetadataAttribute Metadata { get; set; } public TaskMetadataAttribute Metadata { get; set; }
/// <summary> /// <summary>
/// The function used to create the task object. /// The function used to create the task object.
/// </summary> /// </summary>
@ -53,23 +53,23 @@ namespace Kyoo.Core.Controllers
/// The task currently queued. /// The task currently queued.
/// </summary> /// </summary>
public ManagedTask Task { get; init; } public ManagedTask Task { get; init; }
/// <summary> /// <summary>
/// The progress reporter that this task should use. /// The progress reporter that this task should use.
/// </summary> /// </summary>
public IProgress<float> ProgressReporter { get; init; } public IProgress<float> ProgressReporter { get; init; }
/// <summary> /// <summary>
/// The arguments to give to run the task with. /// The arguments to give to run the task with.
/// </summary> /// </summary>
public Dictionary<string, object> Arguments { get; init; } public Dictionary<string, object> Arguments { get; init; }
/// <summary> /// <summary>
/// A token informing the task that it should be cancelled or not. /// A token informing the task that it should be cancelled or not.
/// </summary> /// </summary>
public CancellationToken? CancellationToken { get; init; } public CancellationToken? CancellationToken { get; init; }
} }
/// <summary> /// <summary>
/// The configuration instance used to get schedule information /// The configuration instance used to get schedule information
/// </summary> /// </summary>
@ -115,14 +115,14 @@ namespace Kyoo.Core.Controllers
Metadata = x.Metadata, Metadata = x.Metadata,
ScheduledDate = GetNextTaskDate(x.Metadata.Slug) ScheduledDate = GetNextTaskDate(x.Metadata.Slug)
}).ToList(); }).ToList();
if (_tasks.Any()) if (_tasks.Any())
_logger.LogTrace("Task manager initiated with: {Tasks}", _tasks.Select(x => x.Metadata.Name)); _logger.LogTrace("Task manager initiated with: {Tasks}", _tasks.Select(x => x.Metadata.Name));
else else
_logger.LogInformation("Task manager initiated without any tasks"); _logger.LogInformation("Task manager initiated without any tasks");
} }
/// <summary> /// <summary>
/// Triggered when the application host is ready to start the service. /// Triggered when the application host is ready to start the service.
/// </summary> /// </summary>
@ -133,7 +133,7 @@ namespace Kyoo.Core.Controllers
Task.Run(() => base.StartAsync(cancellationToken), CancellationToken.None); Task.Run(() => base.StartAsync(cancellationToken), CancellationToken.None);
return Task.CompletedTask; return Task.CompletedTask;
} }
/// <inheritdoc /> /// <inheritdoc />
public override Task StopAsync(CancellationToken cancellationToken) public override Task StopAsync(CancellationToken cancellationToken)
{ {
@ -148,7 +148,7 @@ namespace Kyoo.Core.Controllers
protected override async Task ExecuteAsync(CancellationToken cancellationToken) protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{ {
_EnqueueStartupTasks(); _EnqueueStartupTasks();
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
if (_queuedTasks.Any()) if (_queuedTasks.Any())
@ -160,12 +160,12 @@ namespace Kyoo.Core.Controllers
} }
catch (TaskFailedException ex) catch (TaskFailedException ex)
{ {
_logger.LogWarning("The task \"{Task}\" failed: {Message}", _logger.LogWarning("The task \"{Task}\" failed: {Message}",
task.Task.Metadata.Name, ex.Message); task.Task.Metadata.Name, ex.Message);
} }
catch (Exception e) catch (Exception e)
{ {
_logger.LogError(e, "An unhandled exception occured while running the task {Task}", _logger.LogError(e, "An unhandled exception occured while running the task {Task}",
task.Task.Metadata.Name); task.Task.Metadata.Name);
} }
} }
@ -188,7 +188,7 @@ namespace Kyoo.Core.Controllers
/// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument /// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument
/// invalid. /// invalid.
/// </exception> /// </exception>
private async Task _RunTask(ManagedTask task, private async Task _RunTask(ManagedTask task,
[NotNull] IProgress<float> progress, [NotNull] IProgress<float> progress,
Dictionary<string, object> arguments, Dictionary<string, object> arguments,
CancellationToken? cancellationToken = null) CancellationToken? cancellationToken = null)
@ -220,14 +220,14 @@ namespace Kyoo.Core.Controllers
return x.CreateValue(value ?? x.DefaultValue); return x.CreateValue(value ?? x.DefaultValue);
})); }));
_logger.LogInformation("Task starting: {Task} ({Parameters})", _logger.LogInformation("Task starting: {Task} ({Parameters})",
task.Metadata.Name, args.ToDictionary(x => x.Name, x => x.As<object>())); task.Metadata.Name, args.ToDictionary(x => x.Name, x => x.As<object>()));
CancellationToken token = cancellationToken != null CancellationToken token = cancellationToken != null
? CancellationTokenSource.CreateLinkedTokenSource(_taskToken.Token, cancellationToken.Value).Token ? CancellationTokenSource.CreateLinkedTokenSource(_taskToken.Token, cancellationToken.Value).Token
: _taskToken.Token; : _taskToken.Token;
await taskObj.Value.Run(args, progress, token); await taskObj.Value.Run(args, progress, token);
_logger.LogInformation("Task finished: {Task}", task.Metadata.Name); _logger.LogInformation("Task finished: {Task}", task.Metadata.Name);
_runningTask = null; _runningTask = null;
} }
@ -261,13 +261,13 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public void StartTask(string taskSlug, public void StartTask(string taskSlug,
IProgress<float> progress, IProgress<float> progress,
Dictionary<string, object> arguments = null, Dictionary<string, object> arguments = null,
CancellationToken? cancellationToken = null) CancellationToken? cancellationToken = null)
{ {
arguments ??= new Dictionary<string, object>(); arguments ??= new Dictionary<string, object>();
int index = _tasks.FindIndex(x => x.Metadata.Slug == taskSlug); int index = _tasks.FindIndex(x => x.Metadata.Slug == taskSlug);
if (index == -1) if (index == -1)
throw new ItemNotFoundException($"No task found with the slug {taskSlug}"); throw new ItemNotFoundException($"No task found with the slug {taskSlug}");
@ -276,13 +276,13 @@ namespace Kyoo.Core.Controllers
Task = _tasks[index], Task = _tasks[index],
ProgressReporter = progress, ProgressReporter = progress,
Arguments = arguments, Arguments = arguments,
CancellationToken = cancellationToken CancellationToken = cancellationToken
}); });
_tasks[index].ScheduledDate = GetNextTaskDate(taskSlug); _tasks[index].ScheduledDate = GetNextTaskDate(taskSlug);
} }
/// <inheritdoc /> /// <inheritdoc />
public void StartTask<T>(IProgress<float> progress, public void StartTask<T>(IProgress<float> progress,
Dictionary<string, object> arguments = null, Dictionary<string, object> arguments = null,
CancellationToken? cancellationToken = null) CancellationToken? cancellationToken = null)
where T : ITask where T : ITask
@ -304,12 +304,12 @@ namespace Kyoo.Core.Controllers
return DateTime.Now + delay; return DateTime.Now + delay;
return DateTime.MaxValue; return DateTime.MaxValue;
} }
/// <inheritdoc /> /// <inheritdoc />
public ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks() public ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks()
{ {
return _runningTask == null return _runningTask == null
? ArraySegment<(TaskMetadataAttribute, ITask)>.Empty ? ArraySegment<(TaskMetadataAttribute, ITask)>.Empty
: new[] { _runningTask.Value }; : new[] { _runningTask.Value };
} }

View File

@ -28,7 +28,7 @@ namespace Kyoo.Core.Controllers
/// </summary> /// </summary>
/// <param name="files">The file manager to use.</param> /// <param name="files">The file manager to use.</param>
/// <param name="logger">A logger to report errors</param> /// <param name="logger">A logger to report errors</param>
public ThumbnailsManager(IFileSystem files, public ThumbnailsManager(IFileSystem files,
ILogger<ThumbnailsManager> logger) ILogger<ThumbnailsManager> logger)
{ {
_files = files; _files = files;
@ -66,7 +66,7 @@ namespace Kyoo.Core.Controllers
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<bool> DownloadImages<T>(T item, bool alwaysDownload = false) public async Task<bool> DownloadImages<T>(T item, bool alwaysDownload = false)
where T : IThumbnails where T : IThumbnails
{ {
if (item == null) if (item == null)
@ -84,7 +84,7 @@ namespace Kyoo.Core.Controllers
if (alwaysDownload || !await _files.Exists(localPath)) if (alwaysDownload || !await _files.Exists(localPath))
ret |= await _DownloadImage(image, localPath, $"The image n°{id} of {name}"); ret |= await _DownloadImage(image, localPath, $"The image n°{id} of {name}");
} }
return ret; return ret;
} }
@ -99,7 +99,7 @@ namespace Kyoo.Core.Controllers
{ {
if (item == null) if (item == null)
throw new ArgumentNullException(nameof(item)); throw new ArgumentNullException(nameof(item));
string directory = await _files.GetExtraDirectory(item); string directory = await _files.GetExtraDirectory(item);
string imageName = imageID switch string imageName = imageID switch
{ {
@ -109,7 +109,7 @@ namespace Kyoo.Core.Controllers
Images.Trailer => "trailer", Images.Trailer => "trailer",
_ => $"{imageID}" _ => $"{imageID}"
}; };
switch (item) switch (item)
{ {
case Season season: case Season season:
@ -123,7 +123,7 @@ namespace Kyoo.Core.Controllers
return _files.Combine(directory, imageName); return _files.Combine(directory, imageName);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<string> GetImagePath<T>(T item, int imageID) public async Task<string> GetImagePath<T>(T item, int imageID)
where T : IThumbnails where T : IThumbnails

View File

@ -13,8 +13,8 @@ using Stream = Kyoo.Core.Models.Watch.Stream;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers
{ {
public class BadTranscoderException : Exception {} public class BadTranscoderException : Exception { }
public class Transcoder : ITranscoder public class Transcoder : ITranscoder
{ {
private static class TranscoderAPI private static class TranscoderAPI
@ -26,7 +26,7 @@ namespace Kyoo.Core.Controllers
public static int Init() => init(); public static int Init() => init();
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl, [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)] CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern int transmux(string path, string outpath, out float playableDuration); private static extern int transmux(string path, string outpath, out float playableDuration);
@ -37,9 +37,9 @@ namespace Kyoo.Core.Controllers
return transmux(path, outPath, out playableDuration); return transmux(path, outPath, out playableDuration);
} }
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl, [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)] CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr extract_infos(string path, private static extern IntPtr extract_infos(string path,
string outpath, string outpath,
out uint length, out uint length,
out uint trackCount, out uint trackCount,
@ -53,12 +53,12 @@ namespace Kyoo.Core.Controllers
{ {
path = path.Replace('\\', '/'); path = path.Replace('\\', '/');
outPath = outPath.Replace('\\', '/'); outPath = outPath.Replace('\\', '/');
int size = Marshal.SizeOf<Models.Watch.Stream>(); int size = Marshal.SizeOf<Models.Watch.Stream>();
IntPtr ptr = extract_infos(path, outPath, out uint arrayLength, out uint trackCount, reextract); IntPtr ptr = extract_infos(path, outPath, out uint arrayLength, out uint trackCount, reextract);
IntPtr streamsPtr = ptr; IntPtr streamsPtr = ptr;
Track[] tracks; Track[] tracks;
if (trackCount > 0 && ptr != IntPtr.Zero) if (trackCount > 0 && ptr != IntPtr.Zero)
{ {
tracks = new Track[trackCount]; tracks = new Track[trackCount];
@ -113,7 +113,7 @@ namespace Kyoo.Core.Controllers
{ {
if (!File.Exists(episode.Path)) if (!File.Exists(episode.Path))
throw new ArgumentException("Path does not exists. Can't transcode."); throw new ArgumentException("Path does not exists. Can't transcode.");
string folder = Path.Combine(_options.Value.TransmuxPath, episode.Slug); string folder = Path.Combine(_options.Value.TransmuxPath, episode.Slug);
string manifest = Path.Combine(folder, episode.Slug + ".m3u8"); string manifest = Path.Combine(folder, episode.Slug + ".m3u8");
float playableDuration = 0; float playableDuration = 0;
@ -130,7 +130,7 @@ namespace Kyoo.Core.Controllers
await Console.Error.WriteLineAsync($"Access to the path {manifest} is denied. Please change your transmux path in the config."); await Console.Error.WriteLineAsync($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
return null; return null;
} }
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
transmuxFailed = TranscoderAPI.Transmux(episode.Path, manifest, out playableDuration) != 0; transmuxFailed = TranscoderAPI.Transmux(episode.Path, manifest, out playableDuration) != 0;

View File

@ -29,10 +29,10 @@ namespace Kyoo.Core
{ {
/// <inheritdoc /> /// <inheritdoc />
public string Slug => "core"; public string Slug => "core";
/// <inheritdoc /> /// <inheritdoc />
public string Name => "Core"; public string Name => "Core";
/// <inheritdoc /> /// <inheritdoc />
public string Description => "The core module containing default implementations."; public string Description => "The core module containing default implementations.";
@ -66,11 +66,11 @@ namespace Kyoo.Core
public void Configure(ContainerBuilder builder) public void Configure(ContainerBuilder builder)
{ {
builder.RegisterModule<AttributedMetadataModule>(); builder.RegisterModule<AttributedMetadataModule>();
builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope(); builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope();
builder.RegisterType<LocalFileSystem>().As<IFileSystem>().SingleInstance(); builder.RegisterType<LocalFileSystem>().As<IFileSystem>().SingleInstance();
builder.RegisterType<HttpFileSystem>().As<IFileSystem>().SingleInstance(); builder.RegisterType<HttpFileSystem>().As<IFileSystem>().SingleInstance();
builder.RegisterType<TaskManager>().As<ITaskManager>().As<IHostedService>().SingleInstance(); builder.RegisterType<TaskManager>().As<ITaskManager>().As<IHostedService>().SingleInstance();
builder.RegisterType<ConfigurationManager>().As<IConfigurationManager>().SingleInstance(); builder.RegisterType<ConfigurationManager>().As<IConfigurationManager>().SingleInstance();
@ -78,7 +78,7 @@ namespace Kyoo.Core
builder.RegisterType<ThumbnailsManager>().As<IThumbnailsManager>().InstancePerLifetimeScope(); builder.RegisterType<ThumbnailsManager>().As<IThumbnailsManager>().InstancePerLifetimeScope();
builder.RegisterType<LibraryManager>().As<ILibraryManager>().InstancePerLifetimeScope(); builder.RegisterType<LibraryManager>().As<ILibraryManager>().InstancePerLifetimeScope();
builder.RegisterType<RegexIdentifier>().As<IIdentifier>().SingleInstance(); builder.RegisterType<RegexIdentifier>().As<IIdentifier>().SingleInstance();
builder.RegisterComposite<ProviderComposite, IMetadataProvider>(); builder.RegisterComposite<ProviderComposite, IMetadataProvider>();
builder.Register(x => (AProviderComposite)x.Resolve<IMetadataProvider>()); builder.Register(x => (AProviderComposite)x.Resolve<IMetadataProvider>());
@ -106,7 +106,7 @@ namespace Kyoo.Core
builder.RegisterType<PassthroughPermissionValidator>().As<IPermissionValidator>() builder.RegisterType<PassthroughPermissionValidator>().As<IPermissionValidator>()
.IfNotRegistered(typeof(IPermissionValidator)); .IfNotRegistered(typeof(IPermissionValidator));
builder.RegisterType<FileExtensionContentTypeProvider>().As<IContentTypeProvider>().SingleInstance() builder.RegisterType<FileExtensionContentTypeProvider>().As<IContentTypeProvider>().SingleInstance()
.OnActivating(x => .OnActivating(x =>
{ {
@ -117,7 +117,7 @@ namespace Kyoo.Core
x.Instance.Mappings[".m3u8"] = "application/x-mpegurl"; x.Instance.Mappings[".m3u8"] = "application/x-mpegurl";
}); });
} }
/// <inheritdoc /> /// <inheritdoc />
public void Configure(IServiceCollection services) public void Configure(IServiceCollection services)
{ {
@ -130,12 +130,12 @@ namespace Kyoo.Core
x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer(publicUrl); x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer(publicUrl);
x.SerializerSettings.Converters.Add(new PeopleRoleConverter()); x.SerializerSettings.Converters.Add(new PeopleRoleConverter());
}); });
services.AddResponseCompression(x => services.AddResponseCompression(x =>
{ {
x.EnableForHttps = true; x.EnableForHttps = true;
}); });
services.AddHttpClient(); services.AddHttpClient();
} }

View File

@ -17,7 +17,7 @@ namespace Kyoo.Core.Models.Watch
".mkv", ".mkv",
".flv", ".flv",
".vob", ".vob",
".ogg", ".ogg",
".ogv", ".ogv",
".avi", ".avi",
".mts", ".mts",
@ -25,7 +25,7 @@ namespace Kyoo.Core.Models.Watch
".ts", ".ts",
".mov", ".mov",
".qt", ".qt",
".asf", ".asf",
".mp4", ".mp4",
".m4p", ".m4p",
".m4v", ".m4v",
@ -48,11 +48,11 @@ namespace Kyoo.Core.Models.Watch
{ {
return VideoExtensions.Contains(Path.GetExtension(filePath)); return VideoExtensions.Contains(Path.GetExtension(filePath));
} }
/// <summary> /// <summary>
/// The dictionary of known subtitles extensions and the name of the subtitle codec. /// The dictionary of known subtitles extensions and the name of the subtitle codec.
/// </summary> /// </summary>
public static readonly ImmutableDictionary<string, string> SubtitleExtensions = new Dictionary<string, string> public static readonly ImmutableDictionary<string, string> SubtitleExtensions = new Dictionary<string, string>
{ {
{".ass", "ass"}, {".ass", "ass"},
{".str", "subrip"} {".str", "subrip"}

View File

@ -32,7 +32,7 @@ namespace Kyoo.Core.Models.Options
/// The temporary folder to cache transmuxed file. /// The temporary folder to cache transmuxed file.
/// </summary> /// </summary>
public string TransmuxPath { get; set; } = "cached/transmux"; public string TransmuxPath { get; set; } = "cached/transmux";
/// <summary> /// <summary>
/// The temporary folder to cache transcoded file. /// The temporary folder to cache transcoded file.
/// </summary> /// </summary>

View File

@ -9,12 +9,12 @@ namespace Kyoo.Core.Models.Options
/// The path of this options /// The path of this options
/// </summary> /// </summary>
public const string Path = "Media"; public const string Path = "Media";
/// <summary> /// <summary>
/// A regex for episodes /// A regex for episodes
/// </summary> /// </summary>
public string[] Regex { get; set; } public string[] Regex { get; set; }
/// <summary> /// <summary>
/// A regex for subtitles /// A regex for subtitles
/// </summary> /// </summary>

View File

@ -13,7 +13,7 @@ namespace Kyoo.Core.Models.Options
/// The path of this options /// The path of this options
/// </summary> /// </summary>
public const string Path = "Tasks"; public const string Path = "Tasks";
/// <summary> /// <summary>
/// The number of tasks that can be run concurrently. /// The number of tasks that can be run concurrently.
/// </summary> /// </summary>

View File

@ -14,17 +14,17 @@ namespace Kyoo.Core.Models.Watch
/// The title of the stream. /// The title of the stream.
/// </summary> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// The language of this stream (as a ISO-639-2 language code) /// The language of this stream (as a ISO-639-2 language code)
/// </summary> /// </summary>
public string Language { get; set; } public string Language { get; set; }
/// <summary> /// <summary>
/// The codec of this stream. /// The codec of this stream.
/// </summary> /// </summary>
public string Codec { get; set; } public string Codec { get; set; }
/// <summary> /// <summary>
/// Is this stream the default one of it's type? /// Is this stream the default one of it's type?
/// </summary> /// </summary>
@ -34,12 +34,12 @@ namespace Kyoo.Core.Models.Watch
/// Is this stream tagged as forced? /// Is this stream tagged as forced?
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.I1)] public bool IsForced; [MarshalAs(UnmanagedType.I1)] public bool IsForced;
/// <summary> /// <summary>
/// The path of this track. /// The path of this track.
/// </summary> /// </summary>
[SerializeIgnore] public string Path { get; set; } [SerializeIgnore] public string Path { get; set; }
/// <summary> /// <summary>
/// The type of this stream. /// The type of this stream.
/// </summary> /// </summary>

View File

@ -69,7 +69,7 @@ namespace Kyoo.Core
{ {
foreach (IPlugin plugin in _plugins.GetAllPlugins()) foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(services); plugin.Configure(services);
IEnumerable<KeyValuePair<string, Type>> configTypes = _plugins.GetAllPlugins() IEnumerable<KeyValuePair<string, Type>> configTypes = _plugins.GetAllPlugins()
.SelectMany(x => x.Configuration) .SelectMany(x => x.Configuration)
.Where(x => x.Value != null); .Where(x => x.Value != null);
@ -92,7 +92,7 @@ namespace Kyoo.Core
{ {
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned(); builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
builder.RegisterTask<PluginInitializer>(); builder.RegisterTask<PluginInitializer>();
foreach (IPlugin plugin in _plugins.GetAllPlugins()) foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(builder); plugin.Configure(builder);
} }
@ -125,8 +125,8 @@ namespace Kyoo.Core
foreach ((string path, Type type) in pluginConfig) foreach ((string path, Type type) in pluginConfig)
config.Register(path, type); config.Register(path, type);
} }
/// <summary> /// <summary>
/// Create a new <see cref="PluginsStartup"/> from a webhost. /// Create a new <see cref="PluginsStartup"/> from a webhost.
/// This is meant to be used from <see cref="WebHostBuilderExtensions.UseStartup"/>. /// This is meant to be used from <see cref="WebHostBuilderExtensions.UseStartup"/>.
@ -136,7 +136,7 @@ namespace Kyoo.Core
/// The logger factory used to log while the application is setting itself up. /// The logger factory used to log while the application is setting itself up.
/// </param> /// </param>
/// <returns>A new <see cref="PluginsStartup"/>.</returns> /// <returns>A new <see cref="PluginsStartup"/>.</returns>
public static PluginsStartup FromWebHost(WebHostBuilderContext host, public static PluginsStartup FromWebHost(WebHostBuilderContext host,
ILoggerFactory logger) ILoggerFactory logger)
{ {
HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger); HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger);
@ -147,7 +147,7 @@ namespace Kyoo.Core
); );
return new PluginsStartup(plugins, host.Configuration); return new PluginsStartup(plugins, host.Configuration);
} }
/// <summary> /// <summary>
/// A simple host service provider used to activate plugins instance. /// A simple host service provider used to activate plugins instance.
/// The same services as a generic host are available and an <see cref="ILoggerFactory"/> has been added. /// The same services as a generic host are available and an <see cref="ILoggerFactory"/> has been added.
@ -158,18 +158,18 @@ namespace Kyoo.Core
/// The host environment that could be used by plugins to configure themself. /// The host environment that could be used by plugins to configure themself.
/// </summary> /// </summary>
private readonly IWebHostEnvironment _hostEnvironment; private readonly IWebHostEnvironment _hostEnvironment;
/// <summary> /// <summary>
/// The configuration context. /// The configuration context.
/// </summary> /// </summary>
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
/// <summary> /// <summary>
/// A logger factory used to create a logger for the plugin manager. /// A logger factory used to create a logger for the plugin manager.
/// </summary> /// </summary>
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
/// <summary> /// <summary>
/// Create a new <see cref="HostServiceProvider"/> that will return given services when asked. /// Create a new <see cref="HostServiceProvider"/> that will return given services when asked.
/// </summary> /// </summary>

View File

@ -52,8 +52,8 @@ namespace Kyoo.Core.Tasks
_taskManager = taskManager; _taskManager = taskManager;
_logger = logger; _logger = logger;
} }
/// <inheritdoc /> /// <inheritdoc />
public TaskParameters GetParameters() public TaskParameters GetParameters()
{ {
@ -67,9 +67,9 @@ namespace Kyoo.Core.Tasks
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken) public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
{ {
string argument = arguments["slug"].As<string>(); string argument = arguments["slug"].As<string>();
ICollection<Library> libraries = argument == null ICollection<Library> libraries = argument == null
? await _libraryManager.GetAll<Library>() ? await _libraryManager.GetAll<Library>()
: new [] { await _libraryManager.GetOrDefault<Library>(argument)}; : new[] { await _libraryManager.GetOrDefault<Library>(argument) };
if (argument != null && libraries.First() == null) if (argument != null && libraries.First() == null)
throw new ArgumentException($"No library found with the name {argument}"); throw new ArgumentException($"No library found with the name {argument}");
@ -79,7 +79,7 @@ namespace Kyoo.Core.Tasks
progress.Report(0); progress.Report(0);
float percent = 0; float percent = 0;
ICollection<Episode> episodes = await _libraryManager.GetAll<Episode>(); ICollection<Episode> episodes = await _libraryManager.GetAll<Episode>();
ICollection<Track> tracks = await _libraryManager.GetAll<Track>(); ICollection<Track> tracks = await _libraryManager.GetAll<Track>();
foreach (Library library in libraries) foreach (Library library in libraries)
@ -91,15 +91,15 @@ namespace Kyoo.Core.Tasks
}); });
await Scan(library, episodes, tracks, reporter, cancellationToken); await Scan(library, episodes, tracks, reporter, cancellationToken);
percent += 100f / libraries.Count; percent += 100f / libraries.Count;
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
} }
progress.Report(100); progress.Report(100);
} }
private async Task Scan(Library library, private async Task Scan(Library library,
IEnumerable<Episode> episodes, IEnumerable<Episode> episodes,
IEnumerable<Track> tracks, IEnumerable<Track> tracks,
IProgress<float> progress, IProgress<float> progress,
@ -109,7 +109,7 @@ namespace Kyoo.Core.Tasks
foreach (string path in library.Paths) foreach (string path in library.Paths)
{ {
ICollection<string> files = await _fileSystem.ListFiles(path, SearchOption.AllDirectories); ICollection<string> files = await _fileSystem.ListFiles(path, SearchOption.AllDirectories);
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
@ -121,8 +121,8 @@ namespace Kyoo.Core.Tasks
.Where(x => episodes.All(y => y.Path != x)) .Where(x => episodes.All(y => y.Path != x))
.GroupBy(Path.GetDirectoryName) .GroupBy(Path.GetDirectoryName)
.ToList(); .ToList();
string[] paths = shows.Select(x => x.First()) string[] paths = shows.Select(x => x.First())
.Concat(shows.SelectMany(x => x.Skip(1))) .Concat(shows.SelectMany(x => x.Skip(1)))
.ToArray(); .ToArray();
@ -132,7 +132,7 @@ namespace Kyoo.Core.Tasks
// ReSharper disable once AccessToModifiedClosure // ReSharper disable once AccessToModifiedClosure
progress.Report((percent + x / paths.Length - 10) / library.Paths.Length); progress.Report((percent + x / paths.Length - 10) / library.Paths.Length);
}); });
foreach (string episodePath in paths) foreach (string episodePath in paths)
{ {
_taskManager.StartTask<RegisterEpisode>(reporter, new Dictionary<string, object> _taskManager.StartTask<RegisterEpisode>(reporter, new Dictionary<string, object>
@ -143,7 +143,7 @@ namespace Kyoo.Core.Tasks
percent += 100f / paths.Length; percent += 100f / paths.Length;
} }
string[] subtitles = files string[] subtitles = files
.Where(FileExtensions.IsSubtitle) .Where(FileExtensions.IsSubtitle)
.Where(x => !x.Contains("Extra")) .Where(x => !x.Contains("Extra"))
@ -155,7 +155,7 @@ namespace Kyoo.Core.Tasks
// ReSharper disable once AccessToModifiedClosure // ReSharper disable once AccessToModifiedClosure
progress.Report((90 + (percent + x / subtitles.Length)) / library.Paths.Length); progress.Report((90 + (percent + x / subtitles.Length)) / library.Paths.Length);
}); });
foreach (string trackPath in subtitles) foreach (string trackPath in subtitles)
{ {
_taskManager.StartTask<RegisterSubtitle>(reporter, new Dictionary<string, object> _taskManager.StartTask<RegisterSubtitle>(reporter, new Dictionary<string, object>

View File

@ -57,10 +57,10 @@ namespace Kyoo.Core.Tasks
{ {
progress.Report(count / delCount * 100); progress.Report(count / delCount * 100);
count++; count++;
if (await _fileSystem.Exists(show.Path)) if (await _fileSystem.Exists(show.Path))
continue; continue;
_logger.LogWarning("Show {Name}'s folder has been deleted (was {Path}), removing it from kyoo", _logger.LogWarning("Show {Name}'s folder has been deleted (was {Path}), removing it from kyoo",
show.Title, show.Path); show.Title, show.Path);
await _libraryManager.Delete(show); await _libraryManager.Delete(show);
} }
@ -69,14 +69,14 @@ namespace Kyoo.Core.Tasks
{ {
progress.Report(count / delCount * 100); progress.Report(count / delCount * 100);
count++; count++;
if (await _fileSystem.Exists(episode.Path)) if (await _fileSystem.Exists(episode.Path))
continue; continue;
_logger.LogWarning("Episode {Slug}'s file has been deleted (was {Path}), removing it from kyoo", _logger.LogWarning("Episode {Slug}'s file has been deleted (was {Path}), removing it from kyoo",
episode.Slug, episode.Path); episode.Slug, episode.Path);
await _libraryManager.Delete(episode); await _libraryManager.Delete(episode);
} }
progress.Report(100); progress.Report(100);
} }
} }

View File

@ -40,7 +40,7 @@ namespace Kyoo.Core.Tasks
/// <param name="metadataProviders"> /// <param name="metadataProviders">
/// The list of metadata providers to register. /// The list of metadata providers to register.
/// </param> /// </param>
public MetadataProviderLoader(IProviderRepository providers, public MetadataProviderLoader(IProviderRepository providers,
IThumbnailsManager thumbnails, IThumbnailsManager thumbnails,
ICollection<IMetadataProvider> metadataProviders) ICollection<IMetadataProvider> metadataProviders)
{ {
@ -61,7 +61,7 @@ namespace Kyoo.Core.Tasks
{ {
float percent = 0; float percent = 0;
progress.Report(0); progress.Report(0);
foreach (IMetadataProvider provider in _metadataProviders) foreach (IMetadataProvider provider in _metadataProviders)
{ {
if (string.IsNullOrEmpty(provider.Provider.Slug)) if (string.IsNullOrEmpty(provider.Provider.Slug))

View File

@ -10,7 +10,7 @@ namespace Kyoo.Core.Tasks
/// <summary> /// <summary>
/// A task run on Kyoo's startup to initialize plugins /// A task run on Kyoo's startup to initialize plugins
/// </summary> /// </summary>
[TaskMetadata("plugin-init", "Plugin Initializer", "A task to initialize plugins.", [TaskMetadata("plugin-init", "Plugin Initializer", "A task to initialize plugins.",
RunOnStartup = true, Priority = int.MaxValue, IsHidden = true)] RunOnStartup = true, Priority = int.MaxValue, IsHidden = true)]
public class PluginInitializer : ITask public class PluginInitializer : ITask
{ {
@ -33,14 +33,14 @@ namespace Kyoo.Core.Tasks
_pluginManager = pluginManager; _pluginManager = pluginManager;
_provider = provider; _provider = provider;
} }
/// <inheritdoc /> /// <inheritdoc />
public TaskParameters GetParameters() public TaskParameters GetParameters()
{ {
return new(); return new();
} }
/// <inheritdoc /> /// <inheritdoc />
public Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken) public Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
{ {
@ -51,11 +51,11 @@ namespace Kyoo.Core.Tasks
foreach (IPlugin plugin in plugins) foreach (IPlugin plugin in plugins)
{ {
plugin.Initialize(_provider); plugin.Initialize(_provider);
progress.Report(count / plugins.Count * 100); progress.Report(count / plugins.Count * 100);
count++; count++;
} }
progress.Report(100); progress.Report(100);
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@ -76,7 +76,7 @@ namespace Kyoo.Core.Tasks
TaskParameter.CreateRequired<Library>("library", "The library in witch the episode is") TaskParameter.CreateRequired<Library>("library", "The library in witch the episode is")
}; };
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken) public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
{ {
@ -148,7 +148,7 @@ namespace Kyoo.Core.Tasks
throw new TaskFailedException(ex); throw new TaskFailedException(ex);
} }
} }
/// <summary> /// <summary>
/// Retrieve the equivalent item if it already exists in the database, /// Retrieve the equivalent item if it already exists in the database,
/// if it does not, fill metadata using the metadata provider, download images and register the item to the /// if it does not, fill metadata using the metadata provider, download images and register the item to the
@ -172,7 +172,7 @@ namespace Kyoo.Core.Tasks
item = await _metadataProvider.Get(item); item = await _metadataProvider.Get(item);
await _thumbnailsManager.DownloadImages(item); await _thumbnailsManager.DownloadImages(item);
switch (item) switch (item)
{ {
case Show show when show.People != null: case Show show when show.People != null:

View File

@ -42,7 +42,7 @@ namespace Kyoo.Core.Tasks
TaskParameter.CreateRequired<string>("path", "The path of the subtitle file") TaskParameter.CreateRequired<string>("path", "The path of the subtitle file")
}; };
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken) public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
{ {

View File

@ -22,17 +22,17 @@ namespace Kyoo.Core.Api
private readonly IFileSystem _files; private readonly IFileSystem _files;
private readonly IThumbnailsManager _thumbs; private readonly IThumbnailsManager _thumbs;
public CollectionApi(ILibraryManager libraryManager, public CollectionApi(ILibraryManager libraryManager,
IFileSystem files, IFileSystem files,
IThumbnailsManager thumbs, IThumbnailsManager thumbs,
IOptions<BasicOptions> options) IOptions<BasicOptions> options)
: base(libraryManager.CollectionRepository, options.Value.PublicUrl) : base(libraryManager.CollectionRepository, options.Value.PublicUrl)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_files = files; _files = files;
_thumbs = thumbs; _thumbs = thumbs;
} }
[HttpGet("{id:int}/show")] [HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")] [HttpGet("{id:int}/shows")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -55,10 +55,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{slug}/show")] [HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")] [HttpGet("{slug}/shows")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -81,10 +81,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{id:int}/library")] [HttpGet("{id:int}/library")]
[HttpGet("{id:int}/libraries")] [HttpGet("{id:int}/libraries")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -107,10 +107,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{slug}/library")] [HttpGet("{slug}/library")]
[HttpGet("{slug}/libraries")] [HttpGet("{slug}/libraries")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -133,10 +133,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{slug}/poster")] [HttpGet("{slug}/poster")]
public async Task<IActionResult> GetPoster(string slug) public async Task<IActionResult> GetPoster(string slug)
{ {
@ -150,7 +150,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
} }
} }
[HttpGet("{slug}/logo")] [HttpGet("{slug}/logo")]
public async Task<IActionResult> GetLogo(string slug) public async Task<IActionResult> GetLogo(string slug)
{ {
@ -164,7 +164,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
} }
} }
[HttpGet("{slug}/backdrop")] [HttpGet("{slug}/backdrop")]
[HttpGet("{slug}/thumbnail")] [HttpGet("{slug}/thumbnail")]
public async Task<IActionResult> GetBackdrop(string slug) public async Task<IActionResult> GetBackdrop(string slug)

View File

@ -25,7 +25,7 @@ namespace Kyoo.Core.Api
public EpisodeApi(ILibraryManager libraryManager, public EpisodeApi(ILibraryManager libraryManager,
IOptions<BasicOptions> options, IOptions<BasicOptions> options,
IFileSystem files, IFileSystem files,
IThumbnailsManager thumbnails) IThumbnailsManager thumbnails)
: base(libraryManager.EpisodeRepository, options.Value.PublicUrl) : base(libraryManager.EpisodeRepository, options.Value.PublicUrl)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
@ -37,12 +37,12 @@ namespace Kyoo.Core.Api
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(int episodeID) public async Task<ActionResult<Show>> GetShow(int episodeID)
{ {
Show ret = await _libraryManager.GetOrDefault<Show>(x => x.Episodes.Any(y => y.ID == episodeID)); Show ret = await _libraryManager.GetOrDefault<Show>(x => x.Episodes.Any(y => y.ID == episodeID));
if (ret == null) if (ret == null)
return NotFound(); return NotFound();
return ret; return ret;
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")] [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber, int episodeNumber) public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber, int episodeNumber)
@ -52,7 +52,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
return ret; return ret;
} }
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")] [HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber) public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber)
@ -62,7 +62,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
return ret; return ret;
} }
[HttpGet("{episodeID:int}/season")] [HttpGet("{episodeID:int}/season")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(int episodeID) public async Task<ActionResult<Season>> GetSeason(int episodeID)
@ -72,7 +72,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
return ret; return ret;
} }
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")] [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber) public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
@ -86,7 +86,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
} }
} }
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")] [HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber) public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
@ -100,7 +100,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
} }
} }
[HttpGet("{episodeID:int}/track")] [HttpGet("{episodeID:int}/track")]
[HttpGet("{episodeID:int}/tracks")] [HttpGet("{episodeID:int}/tracks")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -123,10 +123,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/track")] [HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/track")]
[HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/tracks")] [HttpGet("{showID:int}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -141,7 +141,7 @@ namespace Kyoo.Core.Api
try try
{ {
ICollection<Track> resources = await _libraryManager.GetAll( ICollection<Track> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Track>(where, x => x.Episode.ShowID == showID ApiHelper.ParseWhere<Track>(where, x => x.Episode.ShowID == showID
&& x.Episode.SeasonNumber == seasonNumber && x.Episode.SeasonNumber == seasonNumber
&& x.Episode.EpisodeNumber == episodeNumber), && x.Episode.EpisodeNumber == episodeNumber),
new Sort<Track>(sortBy), new Sort<Track>(sortBy),
@ -153,10 +153,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/track")] [HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")] [HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -171,7 +171,7 @@ namespace Kyoo.Core.Api
try try
{ {
ICollection<Track> resources = await _libraryManager.GetAll( ICollection<Track> resources = await _libraryManager.GetAll(
ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == slug ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == slug
&& x.Episode.SeasonNumber == seasonNumber && x.Episode.SeasonNumber == seasonNumber
&& x.Episode.EpisodeNumber == episodeNumber), && x.Episode.EpisodeNumber == episodeNumber),
new Sort<Track>(sortBy), new Sort<Track>(sortBy),
@ -183,10 +183,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{id:int}/thumbnail")] [HttpGet("{id:int}/thumbnail")]
[HttpGet("{id:int}/backdrop")] [HttpGet("{id:int}/backdrop")]
public async Task<IActionResult> GetThumb(int id) public async Task<IActionResult> GetThumb(int id)
@ -201,7 +201,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
} }
} }
[HttpGet("{slug}/thumbnail")] [HttpGet("{slug}/thumbnail")]
[HttpGet("{slug}/backdrop")] [HttpGet("{slug}/backdrop")]
public async Task<IActionResult> GetThumb(string slug) public async Task<IActionResult> GetThumb(string slug)
@ -217,4 +217,4 @@ namespace Kyoo.Core.Api
} }
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace Kyoo.Core.Api
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
[HttpGet("{id:int}/show")] [HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")] [HttpGet("{id:int}/shows")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -47,10 +47,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet("{slug}/show")] [HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")] [HttpGet("{slug}/shows")]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
@ -73,7 +73,7 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
} }

View File

@ -21,8 +21,8 @@ namespace Kyoo.Core.Api
} }
return operand(left, right); return operand(left, right);
} }
public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where, public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where,
Expression<Func<T, bool>> defaultWhere = null) Expression<Func<T, bool>> defaultWhere = null)
{ {
if (where == null || where.Count == 0) if (where == null || where.Count == 0)
@ -35,7 +35,7 @@ namespace Kyoo.Core.Api
{ {
if (key == null || desired == null) if (key == null || desired == null)
throw new ArgumentException("Invalid key/value pair. Can't be null."); throw new ArgumentException("Invalid key/value pair. Can't be null.");
string value = desired; string value = desired;
string operand = "eq"; string operand = "eq";
if (desired.Contains(':')) if (desired.Contains(':'))
@ -68,15 +68,15 @@ namespace Kyoo.Core.Api
valueExpr = Expression.Constant(val, property.PropertyType); valueExpr = Expression.Constant(val, property.PropertyType);
} }
Expression condition = operand switch Expression condition = operand switch
{ {
"eq" when isList => ContainsResourceExpression(propertyExpr, value), "eq" when isList => ContainsResourceExpression(propertyExpr, value),
"ctn" => ContainsResourceExpression(propertyExpr, value), "ctn" => ContainsResourceExpression(propertyExpr, value),
"eq" when valueExpr == null => ResourceEqual(propertyExpr, value), "eq" when valueExpr == null => ResourceEqual(propertyExpr, value),
"not" when valueExpr == null => ResourceEqual(propertyExpr, value, true), "not" when valueExpr == null => ResourceEqual(propertyExpr, value, true),
"eq" => Expression.Equal(propertyExpr, valueExpr), "eq" => Expression.Equal(propertyExpr, valueExpr),
"not" => Expression.NotEqual(propertyExpr, valueExpr!), "not" => Expression.NotEqual(propertyExpr, valueExpr!),
"lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr), "lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr),
@ -110,11 +110,11 @@ namespace Kyoo.Core.Api
valueConst = Expression.Constant(value); valueConst = Expression.Constant(value);
} }
return notEqual return notEqual
? Expression.NotEqual(field, valueConst) ? Expression.NotEqual(field, valueConst)
: Expression.Equal(field, valueConst); : Expression.Equal(field, valueConst);
} }
private static Expression ContainsResourceExpression(MemberExpression xProperty, string value) private static Expression ContainsResourceExpression(MemberExpression xProperty, string value)
{ {
// x => x.PROPERTY.Any(y => y.Slug == value) // x => x.PROPERTY.Any(y => y.Slug == value)

View File

@ -54,10 +54,10 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
[HttpGet] [HttpGet]
[PartialPermission(Kind.Read)] [PartialPermission(Kind.Read)]
public virtual async Task<ActionResult<Page<T>>> GetAll([FromQuery] string sortBy, public virtual async Task<ActionResult<Page<T>>> GetAll([FromQuery] string sortBy,
@ -75,7 +75,7 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
} }
@ -98,7 +98,7 @@ namespace Kyoo.Core.Api
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new { Error = ex.Message });
} }
catch (DuplicatedItemException) catch (DuplicatedItemException)
{ {
@ -106,7 +106,7 @@ namespace Kyoo.Core.Api
return Conflict(existing); return Conflict(existing);
} }
} }
[HttpPut] [HttpPut]
[PartialPermission(Kind.Write)] [PartialPermission(Kind.Write)]
public virtual async Task<ActionResult<T>> Edit([FromQuery] bool resetOld, [FromBody] T resource) public virtual async Task<ActionResult<T>> Edit([FromQuery] bool resetOld, [FromBody] T resource)
@ -140,7 +140,7 @@ namespace Kyoo.Core.Api
return NotFound(); return NotFound();
} }
} }
[HttpPut("{slug}")] [HttpPut("{slug}")]
[PartialPermission(Kind.Write)] [PartialPermission(Kind.Write)]
public virtual async Task<ActionResult<T>> Edit(string slug, [FromQuery] bool resetOld, [FromBody] T resource) public virtual async Task<ActionResult<T>> Edit(string slug, [FromQuery] bool resetOld, [FromBody] T resource)
@ -172,7 +172,7 @@ namespace Kyoo.Core.Api
return Ok(); return Ok();
} }
[HttpDelete("{slug}")] [HttpDelete("{slug}")]
[PartialPermission(Kind.Delete)] [PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(string slug) public virtual async Task<IActionResult> Delete(string slug)
@ -188,7 +188,7 @@ namespace Kyoo.Core.Api
return Ok(); return Ok();
} }
[PartialPermission(Kind.Delete)] [PartialPermission(Kind.Delete)]
public virtual async Task<IActionResult> Delete(Dictionary<string, string> where) public virtual async Task<IActionResult> Delete(Dictionary<string, string> where)
{ {

View File

@ -81,7 +81,7 @@ namespace Kyoo.Core.Api
value.Show.People = null; value.Show.People = null;
if (value.People != null) if (value.People != null)
value.People.Roles = null; value.People.Roles = null;
JObject obj = JObject.FromObject((value.ForPeople ? value.People : value.Show)!, serializer); JObject obj = JObject.FromObject((value.ForPeople ? value.People : value.Show)!, serializer);
obj.Add("role", value.Role); obj.Add("role", value.Role);
obj.Add("type", value.Type); obj.Add("type", value.Type);
@ -93,7 +93,7 @@ namespace Kyoo.Core.Api
value.People.Roles = oldRoles; value.People.Roles = oldRoles;
} }
public override PeopleRole ReadJson(JsonReader reader, public override PeopleRole ReadJson(JsonReader reader,
Type objectType, Type objectType,
PeopleRole existingValue, PeopleRole existingValue,
bool hasExistingValue, bool hasExistingValue,
@ -113,7 +113,7 @@ namespace Kyoo.Core.Api
_format = format; _format = format;
_host = host.TrimEnd('/'); _host = host.TrimEnd('/');
} }
public object GetValue(object target) public object GetValue(object target)
{ {
return Regex.Replace(_format, @"(?<!{){(\w+)(:(\w+))?}", x => return Regex.Replace(_format, @"(?<!{){(\w+)(:(\w+))?}", x =>
@ -123,7 +123,7 @@ namespace Kyoo.Core.Api
if (value == "HOST") if (value == "HOST")
return _host; return _host;
PropertyInfo properties = target.GetType() PropertyInfo properties = target.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.FirstOrDefault(y => y.Name == value); .FirstOrDefault(y => y.Name == value);
@ -147,7 +147,7 @@ namespace Kyoo.Core.Api
return ret; return ret;
}); });
} }
public void SetValue(object target, object value) public void SetValue(object target, object value)
{ {
// Values are ignored and should not be editable, except if the internal value is set. // Values are ignored and should not be editable, except if the internal value is set.

Some files were not shown because too many files have changed in this diff Show More