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
indent_style = tab
indent_size = tab
smart_tab = true
[{*.yaml,*.yml}]
indent_style = space

View File

@ -6,12 +6,15 @@
</PropertyGroup>
<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).editorconfig" Link="stylecop.json" Visible="false" />
<None Include="$(MSBuildThisFileDirectory).editorconfig" Link=".editorconfig" Visible="false" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Kyoo.ruleset</CodeAnalysisRuleSet>
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->
</PropertyGroup>
</Project>

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ namespace Kyoo.Abstractions.Controllers
Count = count;
AfterID = afterID;
}
/// <summary>
/// Implicitly create a new pagination from a limit number.
/// </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.
/// </summary>
public bool Descendant { get; }
/// <summary>
/// Create a new <see cref="Sort{T}"/> instance.
/// </summary>
@ -68,7 +68,7 @@ namespace Kyoo.Abstractions.Controllers
{
Key = key;
Descendant = descendant;
if (!Utility.IsPropertyExpression(Key))
throw new ArgumentException("The given sort key is not valid.");
}
@ -86,7 +86,7 @@ namespace Kyoo.Abstractions.Controllers
Descendant = false;
return;
}
string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy;
string order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null;
@ -116,7 +116,7 @@ namespace Kyoo.Abstractions.Controllers
/// </summary>
Type RepositoryType { get; }
}
/// <summary>
/// A common repository for every resources.
/// </summary>
@ -147,7 +147,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns>
[ItemNotNull]
Task<T> Get(Expression<Func<T, bool>> where);
/// <summary>
/// Get a resource from it's ID or null if it is not found.
/// </summary>
@ -169,7 +169,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource found</returns>
[ItemCanBeNull]
Task<T> GetOrDefault(Expression<Func<T, bool>> where);
/// <summary>
/// Search for resources.
/// </summary>
@ -177,7 +177,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>A list of resources found</returns>
[ItemNotNull]
Task<ICollection<T>> Search(string query);
/// <summary>
/// Get every resources that match all filters
/// </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>
/// <returns>A list of resources that match every filters</returns>
[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,
Pagination limit = default);
/// <summary>
@ -208,8 +208,8 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="where">A filter predicate</param>
/// <returns>How many resources matched that filter</returns>
Task<int> GetCount(Expression<Func<T, bool>> where = null);
/// <summary>
/// Create a new resource.
/// </summary>
@ -217,7 +217,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource registers and completed by database's information (related items & so on)</returns>
[ItemNotNull]
Task<T> Create([NotNull] T obj);
/// <summary>
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
/// </summary>
@ -225,7 +225,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The newly created item or the existing value if it existed.</returns>
[ItemNotNull]
Task<T> CreateIfNotExists([NotNull] T obj);
/// <summary>
/// Edit a resource
/// </summary>
@ -235,7 +235,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The resource edited and completed by database's information (related items & so on)</returns>
[ItemNotNull]
Task<T> Edit([NotNull] T edited, bool resetOld);
/// <summary>
/// Delete a resource by it's ID
/// </summary>
@ -254,7 +254,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="obj">The resource to delete</param>
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
Task Delete([NotNull] T obj);
/// <summary>
/// Delete all resources that match the predicate.
/// </summary>
@ -299,7 +299,7 @@ namespace Kyoo.Abstractions.Controllers
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(int showID, int seasonNumber);
/// <summary>
/// Get a season from it's show slug and it's seasonNumber
/// </summary>
@ -308,7 +308,7 @@ namespace Kyoo.Abstractions.Controllers
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
/// <returns>The season found</returns>
Task<Season> Get(string showSlug, int seasonNumber);
/// <summary>
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
/// </summary>
@ -316,7 +316,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns>
Task<Season> GetOrDefault(int showID, int seasonNumber);
/// <summary>
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
/// </summary>
@ -325,7 +325,7 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>The season found</returns>
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
}
/// <summary>
/// The repository to handle episodes
/// </summary>
@ -366,7 +366,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="episodeNumber">The episode's number</param>
/// <returns>The episode found</returns>
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
/// <summary>
/// Get a episode from it's showID and it's absolute number.
/// </summary>
@ -389,7 +389,7 @@ namespace Kyoo.Abstractions.Controllers
/// A repository to handle tracks
/// </summary>
public interface ITrackRepository : IRepository<Track> { }
/// <summary>
/// A repository to handle libraries.
/// </summary>
@ -425,7 +425,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, object>> sort,
Pagination limit = default
) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
/// <summary>
/// Get items (A wrapper around shows or collections) from a library.
/// </summary>
@ -451,18 +451,18 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<LibraryItem, object>> sort,
Pagination limit = default
) => GetFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
}
}
/// <summary>
/// A repository for collections
/// </summary>
public interface ICollectionRepository : IRepository<Collection> { }
/// <summary>
/// A repository for genres.
/// </summary>
public interface IGenreRepository : IRepository<Genre> { }
/// <summary>
/// A repository for studios.
/// </summary>
@ -482,7 +482,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
/// <summary>
@ -498,7 +498,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
/// <summary>
/// Get people's roles from a show.
/// </summary>
@ -508,7 +508,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
/// <summary>
@ -524,7 +524,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
/// <summary>
/// Get people's roles from a person.
/// </summary>
@ -534,7 +534,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
/// <summary>
@ -550,7 +550,7 @@ namespace Kyoo.Abstractions.Controllers
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
/// <summary>
/// Get people's roles from a person.
/// </summary>
@ -560,7 +560,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">How many items to return and where to start</param>
/// <returns>A list of items that match every filters</returns>
Task<ICollection<PeopleRole>> GetFromPeople(string slug,
Expression<Func<PeopleRole, bool>> where = null,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
/// <summary>
@ -591,7 +591,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="limit">Pagination information (where to start and how many to get)</param>
/// <typeparam name="T">The type of metadata to retrieve</typeparam>
/// <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,
Pagination limit = default)
where T : class, IMetadata;
@ -609,9 +609,9 @@ namespace Kyoo.Abstractions.Controllers
) where T : class, IMetadata
=> GetMetadataID<T>(where, new Sort<MetadataID>(sort), limit);
}
/// <summary>
/// A repository to handle users.
/// </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.
/// </summary>
public string Name { get; init; }
/// <summary>
/// The description of this parameter.
/// </summary>
public string Description { get; init; }
/// <summary>
/// The type of this parameter.
/// </summary>
public Type Type { get; init; }
/// <summary>
/// Is this parameter required or can it be ignored?
/// </summary>
public bool IsRequired { get; init; }
/// <summary>
/// The default value of this object.
/// </summary>
@ -44,7 +44,7 @@ namespace Kyoo.Abstractions.Controllers
/// The value of the parameter.
/// </summary>
private object Value { get; init; }
/// <summary>
/// Create a new task parameter.
/// </summary>
@ -61,7 +61,7 @@ namespace Kyoo.Abstractions.Controllers
Type = typeof(T)
};
}
/// <summary>
/// Create a new required task parameter.
/// </summary>
@ -79,7 +79,7 @@ namespace Kyoo.Abstractions.Controllers
IsRequired = true
};
}
/// <summary>
/// Create a parameter's value to give to a task.
/// </summary>
@ -104,9 +104,9 @@ namespace Kyoo.Abstractions.Controllers
/// <returns>A new parameter's value for this current parameter</returns>
public TaskParameter CreateValue(object value)
{
return this with {Value = value};
return this with { Value = value };
}
/// <summary>
/// Get the value of this parameter. If the value is of the wrong type, it will be converted.
/// </summary>
@ -140,12 +140,12 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="name">The name of the task (case sensitive)</param>
public TaskParameter this[string name] => this.FirstOrDefault(x => x.Name == name);
/// <summary>
/// Create a new, empty, <see cref="TaskParameters"/>
/// </summary>
public TaskParameters() {}
public TaskParameters() { }
/// <summary>
/// Create a <see cref="TaskParameters"/> with an initial parameters content
/// </summary>
@ -155,7 +155,7 @@ namespace Kyoo.Abstractions.Controllers
AddRange(parameters);
}
}
/// <summary>
/// A common interface that tasks should implement.
/// </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.
/// </returns>
public TaskParameters GetParameters();
/// <summary>
/// Start this task.
/// </summary>
@ -191,4 +191,4 @@ namespace Kyoo.Abstractions.Controllers
[NotNull] IProgress<float> progress,
CancellationToken cancellationToken);
}
}
}

View File

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

View File

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

View File

@ -23,9 +23,9 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <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);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
@ -33,9 +33,9 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <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);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
@ -44,9 +44,9 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T">A 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>
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);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
@ -56,11 +56,11 @@ namespace Kyoo.Abstractions.Controllers
/// <typeparam name="T2">A second 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>
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);
}
/// <summary>
/// An action executed on kyoo's startup to initialize the asp-net container.
/// </summary>
@ -81,7 +81,7 @@ namespace Kyoo.Abstractions.Controllers
/// <param name="provider">The service provider containing all services can be used.</param>
void Run(IServiceProvider provider);
}
/// <summary>
/// A <see cref="IStartupAction"/> with no dependencies.
/// </summary>
@ -94,7 +94,7 @@ namespace Kyoo.Abstractions.Controllers
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
@ -126,7 +126,7 @@ namespace Kyoo.Abstractions.Controllers
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T}"/>.
/// </summary>
@ -144,7 +144,7 @@ namespace Kyoo.Abstractions.Controllers
_action.Invoke(provider.GetRequiredService<T>());
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with two dependencies.
/// </summary>
@ -180,7 +180,7 @@ namespace Kyoo.Abstractions.Controllers
);
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with three dependencies.
/// </summary>
@ -196,7 +196,7 @@ namespace Kyoo.Abstractions.Controllers
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2, T3}"/>.
/// </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.
/// </remarks>
public string[] Scheme { get; }
/// <summary>
/// <c>true</c> if the scheme should be removed from the path before calling
/// methods of this <see cref="IFileSystem"/>, <c>false</c> otherwise.
/// </summary>
public bool StripScheme { get; set; }
/// <summary>
/// Create a new <see cref="FileSystemMetadataAttribute"/> using the specified schemes.
@ -36,7 +36,7 @@ namespace Kyoo.Abstractions.Models.Attributes
{
Scheme = schemes;
}
/// <summary>
/// Create a new <see cref="FileSystemMetadataAttribute"/> using a dictionary of metadata.
/// </summary>

View File

@ -23,7 +23,7 @@ namespace Kyoo.Abstractions.Models.Permissions
Overall,
Admin
}
/// <summary>
/// Specify permissions needed for the API.
/// </summary>
@ -63,7 +63,7 @@ namespace Kyoo.Abstractions.Models.Permissions
Kind = permission;
Group = group;
}
/// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
@ -97,7 +97,7 @@ namespace Kyoo.Abstractions.Models.Permissions
/// The needed permission kind.
/// </summary>
public Kind Kind { get; }
/// <summary>
/// Ask a permission to run an action.
/// </summary>
@ -118,7 +118,7 @@ namespace Kyoo.Abstractions.Models.Permissions
type = type[..^3];
Type = type.ToLower();
}
/// <summary>
/// Ask a permission to run an action.
/// </summary>
@ -134,7 +134,7 @@ namespace Kyoo.Abstractions.Models.Permissions
{
Kind = permission;
}
/// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
@ -158,7 +158,7 @@ namespace Kyoo.Abstractions.Models.Permissions
/// <param name="attribute">The permission attribute to validate</param>
/// <returns>An authorization filter used to validate the permission</returns>
IFilterMetadata Create(PermissionAttribute attribute);
/// <summary>
/// Create an IAuthorizationFilter that will be used to validate permissions.
/// 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.
/// </summary>
public string RelationID { get; }
/// <summary>
/// Create a new <see cref="LoadableRelationAttribute"/>.
/// </summary>
public LoadableRelationAttribute() {}
public LoadableRelationAttribute() { }
/// <summary>
/// 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.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class SerializeIgnoreAttribute : Attribute {}
public class SerializeIgnoreAttribute : Attribute { }
/// <summary>
/// Remove a property from the deserialization pipeline. The user can't input value for this property.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class DeserializeIgnoreAttribute : Attribute {}
public class DeserializeIgnoreAttribute : Attribute { }
/// <summary>
/// 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.
/// </summary>
public string Format { get; }
/// <summary>
/// Create a new <see cref="SerializeAsAttribute"/> with the selected format.
/// </summary>

View File

@ -16,12 +16,12 @@ namespace Kyoo.Abstractions.Models.Attributes
/// The slug of the task, used to start it.
/// </summary>
public string Slug { get; }
/// <summary>
/// The name of the task that will be displayed to the user.
/// </summary>
public string Name { get; }
/// <summary>
/// A quick description of what this task will do.
/// </summary>
@ -31,18 +31,18 @@ namespace Kyoo.Abstractions.Models.Attributes
/// Should this task be automatically run at app startup?
/// </summary>
public bool RunOnStartup { get; set; }
/// <summary>
/// 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.
/// </summary>
public int Priority { get; set; }
/// <summary>
/// <c>true</c> if this task should not be displayed to the user, <c>false</c> otherwise.
/// </summary>
public bool IsHidden { get; set; }
/// <summary>
/// Create a new <see cref="TaskMetadataAttribute"/> with the given slug, name and description.
@ -56,7 +56,7 @@ namespace Kyoo.Abstractions.Models.Attributes
Name = name;
Description = description;
}
/// <summary>
/// Create a new <see cref="TaskMetadataAttribute"/> using a dictionary of metadata.
/// </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).
/// </summary>
public float StartTime { get; set; }
/// <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>
public float EndTime { get; set; }
/// <summary>
/// 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.
/// 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>
public string Name { get; set; }
@ -35,4 +35,4 @@ namespace Kyoo.Abstractions.Models
Name = name;
}
}
}
}

View File

@ -21,7 +21,7 @@ namespace Kyoo.Abstractions.Models
/// </summary>
public Type Type { get; }
/// <summary>
/// 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"/>
@ -75,7 +75,7 @@ namespace Kyoo.Abstractions.Models
return ret;
}
/// <summary>
/// Return the list of configuration reference a type has.
/// </summary>

View File

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

View File

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

View File

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

View File

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

View File

@ -12,10 +12,10 @@ namespace Kyoo.Abstractions.Models
{
/// <inheritdoc />
public int ID { get; set; }
/// <inheritdoc />
public string Slug => ForPeople ? Show.Slug : People.Slug;
/// <summary>
/// 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>).
@ -30,7 +30,7 @@ namespace Kyoo.Abstractions.Models
/// The people that played this role.
/// </summary>
public People People { get; set; }
/// <summary>
/// The ID of the Show where the People playing in.
/// </summary>
@ -39,13 +39,13 @@ namespace Kyoo.Abstractions.Models
/// The show where the People played in.
/// </summary>
public Show Show { get; set; }
/// <summary>
/// The type of work the person has done for the show.
/// That can be something like "Actor", "Writer", "Music", "Voice Actor"...
/// </summary>
public string Type { get; set; }
/// <summary>
/// The role the People played.
/// 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 />
public int ID { get; set; }
/// <inheritdoc />
public string Slug { get; set; }
/// <summary>
/// The name of this collection.
/// </summary>
@ -23,7 +23,7 @@ namespace Kyoo.Abstractions.Models
/// <inheritdoc />
public Dictionary<int, string> Images { get; set; }
/// <summary>
/// The path of this poster.
/// 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.
/// </summary>
public string Overview { get; set; }
/// <summary>
/// The list of shows contained in this collection.
/// </summary>
[LoadableRelation] public ICollection<Show> Shows { get; set; }
/// <summary>
/// The list of libraries that contains this collection.
/// </summary>
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
/// <inheritdoc />
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
}

View File

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

View File

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

View File

@ -14,7 +14,8 @@ namespace Kyoo.Abstractions.Models
/// <summary>
/// The link to metadata providers that this show has. See <see cref="MetadataID"/> for more information.
/// </summary>
[EditableRelation] [LoadableRelation]
[EditableRelation]
[LoadableRelation]
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}"/>.
/// </remarks>
public int ID { get; set; }
/// <summary>
/// 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.
@ -24,6 +24,6 @@ namespace Kyoo.Abstractions.Models
/// There is no setter for a slug since it can be computed from other fields.
/// For example, a season slug is {ShowSlug}-s{SeasonNumber}.
/// </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"/>
/// </remarks>
public Dictionary<int, string> Images { get; set; }
// TODO remove Posters properties add them via the json serializer for every IThumbnails
}

View File

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

View File

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

View File

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

View File

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

View File

@ -12,31 +12,31 @@ namespace Kyoo.Abstractions.Models
{
/// <inheritdoc />
public int ID { get; set; }
/// <inheritdoc />
public string Slug { get; set; }
/// <summary>
/// The title of this show.
/// </summary>
public string Title { get; set; }
/// <summary>
/// The list of alternative titles of this show.
/// </summary>
[EditableRelation] public string[] Aliases { get; set; }
/// <summary>
/// The path of the root directory of this show.
/// This can be any kind of path supported by <see cref="IFileSystem"/>
/// </summary>
[SerializeIgnore] public string Path { get; set; }
/// <summary>
/// The summary of this show.
/// </summary>
public string Overview { get; set; }
/// <summary>
/// Is this show airing, not aired yet or finished?
/// </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.
[Obsolete("Use Images instead of this, this is only kept for the API response.")]
public string TrailerUrl => Images?.GetValueOrDefault(Models.Images.Trailer);
/// <summary>
/// The date this show started airing. It can be null if this is unknown.
/// </summary>
public DateTime? StartAir { get; set; }
/// <summary>
/// The date this show finished airing.
/// 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"/>.
/// </summary>
[LoadableRelation(nameof(StudioID))] [EditableRelation] public Studio Studio { get; set; }
/// <summary>
/// The list of genres (themes) this show has.
/// </summary>
[LoadableRelation] [EditableRelation] public ICollection<Genre> Genres { get; set; }
/// <summary>
/// The list of people that made this show.
/// </summary>
[LoadableRelation] [EditableRelation] public ICollection<PeopleRole> People { get; set; }
/// <summary>
/// The different seasons in this show. If this is a movie, this list is always null or empty.
/// </summary>
[LoadableRelation] public ICollection<Season> Seasons { get; set; }
/// <summary>
/// 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).
/// Having an episode is necessary to store metadata and tracks.
/// </summary>
[LoadableRelation] public ICollection<Episode> Episodes { get; set; }
/// <summary>
/// The list of libraries that contains this show.
/// </summary>
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
/// <summary>
/// The list of collections that contains this show.
/// </summary>
[LoadableRelation] public ICollection<Collection> Collections { get; set; }
/// <inheritdoc />
public void OnMerge(object merged)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ namespace Kyoo.Abstractions
/// <param name="builder">The container</param>
/// <typeparam name="T">The type of the task</typeparam>
/// <returns>The registration builder of this new provider. That can be used to edit the registration.</returns>
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
RegisterProvider<T>(this ContainerBuilder builder)
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}"/>
/// </remarks>
/// <returns>The initial container.</returns>
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
RegisterRepository<T>(this ContainerBuilder builder)
where T : IBaseRepository
{

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
@ -32,10 +33,10 @@ namespace Kyoo.Authentication
{
/// <inheritdoc />
public string Slug => "auth";
/// <inheritdoc />
public string Name => "Authentication";
/// <inheritdoc />
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="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>
[SuppressMessage("ReSharper", "ContextualLoggerProblem",
Justification = "The logger is used for a dependency that is not created via the container.")]
public AuthenticationModule(IConfiguration configuration,
ILogger<DefaultCorsPolicyService> logger,
ILogger<DefaultCorsPolicyService> logger,
IWebHostEnvironment environment)
{
_configuration = configuration;
@ -100,16 +103,16 @@ namespace Kyoo.Authentication
IdentityModelEventSource.ShowPII = true;
services.AddControllers();
// 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.
List<Client> clients = new();
_configuration.GetSection("authentication:clients").Bind(clients);
CertificateOption certificateOptions = new();
_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
clients.AddRange(IdentityContext.GetClients());
foreach (Client client in clients)
{
@ -131,7 +134,7 @@ namespace Kyoo.Authentication
.AddInMemoryClients(clients)
.AddProfileService<AccountApi>()
.AddSigninKeys(certificateOptions);
services.AddAuthentication()
.AddJwtBearer(options =>
{
@ -181,4 +184,4 @@ namespace Kyoo.Authentication
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="options">The certificate options</param>
/// <returns></returns>
public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder,
public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder,
CertificateOption options)
{
X509Certificate2 certificate = GetCertificate(options);
builder.AddSigningCredential(certificate);
if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow)
{
Console.WriteLine("Signin certificate will expire soon, renewing it.");
@ -54,8 +54,8 @@ namespace Kyoo.Authentication
/// <returns>A valid certificate</returns>
private static X509Certificate2 GetCertificate(CertificateOption options)
{
return File.Exists(options.File)
? GetExistingCredential(options.File, options.Password)
return File.Exists(options.File)
? GetExistingCredential(options.File, options.Password)
: GenerateCertificate(options.File, options.Password);
}
@ -83,19 +83,19 @@ namespace Kyoo.Authentication
private static X509Certificate2 GenerateCertificate(string file, string password)
{
SecureRandom random = new();
X509V3CertificateGenerator certificateGenerator = new();
certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One,
certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One,
BigInteger.ValueOf(long.MaxValue), random));
certificateGenerator.SetIssuerDN(new X509Name($"C=NL, O=SDG, CN=Kyoo"));
certificateGenerator.SetSubjectDN(new X509Name($"C=NL, O=SDG, CN=Kyoo"));
certificateGenerator.SetNotBefore(DateTime.UtcNow.Date);
certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddMonths(3));
KeyGenerationParameters keyGenerationParameters = new(random, 2048);
RsaKeyPairGenerator keyPairGenerator = new();
keyPairGenerator.Init(keyGenerationParameters);
AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
@ -104,7 +104,7 @@ namespace Kyoo.Authentication
X509Certificate bouncyCert = certificateGenerator.Generate(signatureFactory);
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)
});

View File

@ -15,7 +15,7 @@ namespace Kyoo.Authentication
{
/// <summary>
/// A permission validator to validate permission with user Permission array
/// or the default array from the configurations if the user is not logged.
/// or the default array from the configurations if the user is not logged.
/// </summary>
public class PermissionValidatorFactory : IPermissionValidator
{
@ -38,7 +38,7 @@ namespace Kyoo.Authentication
{
return new PermissionValidator(attribute.Type, attribute.Kind, attribute.Group, _options);
}
/// <inheritdoc />
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())
{
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>
[EmailAddress(ErrorMessage = "The email is invalid.")]
public string Email { get; set; }
/// <summary>
/// The new username of the user.
/// </summary>
[MinLength(4, ErrorMessage = "The username must have at least 4 characters")]
public string Username { get; set; }
/// <summary>
/// The picture icon.
/// </summary>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -57,8 +57,8 @@ namespace Kyoo.Authentication.Views
_files = files;
_options = options;
}
/// <summary>
/// Register a new user and return a OTAC to connect to it.
/// </summary>
@ -78,10 +78,10 @@ namespace Kyoo.Authentication.Views
}
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>
@ -99,7 +99,7 @@ namespace Kyoo.Authentication.Views
ExpiresUtc = DateTimeOffset.UtcNow.AddMonths(1)
};
}
/// <summary>
/// Login the user.
/// </summary>
@ -117,7 +117,7 @@ namespace Kyoo.Authentication.Views
await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(login.StayLoggedIn));
return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true });
}
/// <summary>
/// Use a OTAC to login a user.
/// </summary>
@ -127,22 +127,23 @@ namespace Kyoo.Authentication.Views
{
// TODO once hstore (Dictionary<string, string> accessor) are supported, use them.
// 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)
return Unauthorized();
if (DateTime.ParseExact(user.ExtraData["otac-expire"], "s", CultureInfo.InvariantCulture) <=
DateTime.UtcNow)
DateTime.UtcNow)
{
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));
return Ok();
}
/// <summary>
/// Sign out an user
/// </summary>
@ -170,7 +171,7 @@ namespace Kyoo.Authentication.Views
User user = await _users.GetOrDefault(int.Parse(context.Subject.GetSubjectId()));
context.IsActive = user != null;
}
/// <summary>
/// Get the user's profile picture.
/// </summary>
@ -185,7 +186,7 @@ namespace Kyoo.Authentication.Views
string path = Path.Combine(_options.Value.ProfilePicturePath, user.ID.ToString());
return _files.FileResult(path);
}
/// <summary>
/// Update profile information (email, username, profile picture...)
/// </summary>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -40,7 +40,7 @@ namespace Kyoo.Core.Controllers
_providers = providers.ToArray();
_logger = logger;
}
/// <inheritdoc />
public override void UseProviders(IEnumerable<Provider> providers)
@ -71,9 +71,9 @@ namespace Kyoo.Core.Controllers
{
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);
}
}
@ -85,16 +85,16 @@ namespace Kyoo.Core.Controllers
public override async Task<ICollection<T>> Search<T>(string query)
{
List<T> ret = new();
foreach (IMetadataProvider provider in _GetProviders())
{
try
{
ret.AddRange(await provider.Search<T>(query));
}
catch (Exception ex)
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);
}
}

View File

@ -53,7 +53,7 @@ namespace Kyoo.Core.Controllers
.FirstOrDefault();
return path[(libraryPath?.Length ?? 0)..];
}
/// <inheritdoc />
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),
Title = match.Groups["Show"].Value,
Path = Path.GetDirectoryName(path),
StartAir = match.Groups["StartYear"].Success
? new DateTime(int.Parse(match.Groups["StartYear"].Value), 1, 1)
StartAir = match.Groups["StartYear"].Success
? new DateTime(int.Parse(match.Groups["StartYear"].Value), 1, 1)
: null
},
season: null,
episode: new Episode
{
SeasonNumber = match.Groups["Season"].Success
? int.Parse(match.Groups["Season"].Value)
SeasonNumber = match.Groups["Season"].Success
? int.Parse(match.Groups["Season"].Value)
: null,
EpisodeNumber = match.Groups["Episode"].Success
? int.Parse(match.Groups["Episode"].Value)
EpisodeNumber = match.Groups["Episode"].Success
? int.Parse(match.Groups["Episode"].Value)
: null,
AbsoluteNumber = match.Groups["Absolute"].Success
? int.Parse(match.Groups["Absolute"].Value)
AbsoluteNumber = match.Groups["Absolute"].Success
? int.Parse(match.Groups["Absolute"].Value)
: null,
Path = path
}

View File

@ -19,12 +19,12 @@ namespace Kyoo.Core.Controllers
/// The database handle
/// </summary>
private readonly DatabaseContext _database;
/// <summary>
/// A provider repository to handle externalID creation and deletion
/// </summary>
private readonly IProviderRepository _providers;
/// <inheritdoc />
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).");
return obj;
}
/// <inheritdoc />
protected override async Task Validate(Collection resource)
{
await base.Validate(resource);
if (string.IsNullOrEmpty(resource.Slug))
throw new ArgumentException("The collection's slug must be set and not empty");
if (string.IsNullOrEmpty(resource.Name))
@ -72,7 +72,7 @@ namespace Kyoo.Core.Controllers
if (resource.ExternalIDs != null)
{
foreach (MetadataID id in resource.ExternalIDs)
{
{
id.Provider = _database.LocalEntity<Provider>(id.Provider.Slug)
?? await _providers.CreateIfNotExists(id.Provider);
id.ProviderID = id.Provider.ID;
@ -80,12 +80,12 @@ namespace Kyoo.Core.Controllers
_database.MetadataIds<Collection>().AttachRange(resource.ExternalIDs);
}
}
/// <inheritdoc />
protected override async Task EditRelations(Collection resource, Collection changed, bool resetOld)
{
await Validate(changed);
if (changed.ExternalIDs != null || resetOld)
{
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.
/// </summary>
private readonly ITrackRepository _tracks;
/// <inheritdoc />
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>
public EpisodeRepository(DatabaseContext database,
IProviderRepository providers,
ITrackRepository tracks)
ITrackRepository tracks)
: base(database)
{
_database = database;
_providers = providers;
_tracks = tracks;
}
/// <inheritdoc />
public Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
{
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
&& x.SeasonNumber == seasonNumber
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
&& x.SeasonNumber == seasonNumber
&& x.EpisodeNumber == episodeNumber);
}
/// <inheritdoc />
public Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
{
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
&& x.SeasonNumber == seasonNumber
&& x.EpisodeNumber == episodeNumber);
}
@ -87,14 +87,14 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
@ -108,7 +108,7 @@ namespace Kyoo.Core.Controllers
.Take(20)
.ToListAsync();
}
/// <inheritdoc />
public override async Task<Episode> Create(Episode obj)
{
@ -146,7 +146,7 @@ namespace Kyoo.Core.Controllers
{
if (resource.Tracks == null)
return resource;
resource.Tracks = await resource.Tracks.SelectAsync(x =>
{
x.Episode = resource;
@ -156,7 +156,7 @@ namespace Kyoo.Core.Controllers
_database.Tracks.AttachRange(resource.Tracks);
return resource;
}
/// <inheritdoc />
protected override async Task Validate(Episode resource)
{
@ -180,17 +180,17 @@ namespace Kyoo.Core.Controllers
_database.MetadataIds<Episode>().AttachRange(resource.ExternalIDs);
}
}
/// <inheritdoc />
public override async Task Delete(Episode obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted;
await obj.Tracks.ForEachAsync(x => _tracks.Delete(x));
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
await _database.SaveChangesAsync();
}
}
}
}

View File

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

View File

@ -34,7 +34,7 @@ namespace Kyoo.Core.Controllers
/// </summary>
/// <param name="database">The database instance</param>
/// <param name="libraries">A lazy loaded library repository</param>
public LibraryItemRepository(DatabaseContext database,
public LibraryItemRepository(DatabaseContext database,
Lazy<ILibraryRepository> libraries)
: base(database)
{
@ -42,13 +42,13 @@ namespace Kyoo.Core.Controllers
_libraries = libraries;
}
/// <inheritdoc />
public override Task<LibraryItem> GetOrDefault(int id)
{
return _database.LibraryItems.FirstOrDefaultAsync(x => x.ID == id);
}
/// <inheritdoc />
public override Task<LibraryItem> GetOrDefault(string slug)
{
@ -83,27 +83,27 @@ namespace Kyoo.Core.Controllers
}
/// <inheritdoc />
public override Task<LibraryItem> Create(LibraryItem obj)
public override Task<LibraryItem> Create(LibraryItem obj)
=> throw new InvalidOperationException();
/// <inheritdoc />
public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj)
public override Task<LibraryItem> CreateIfNotExists(LibraryItem obj)
=> throw new InvalidOperationException();
/// <inheritdoc />
public override Task<LibraryItem> Edit(LibraryItem obj, bool resetOld)
public override Task<LibraryItem> Edit(LibraryItem obj, bool resetOld)
=> throw new InvalidOperationException();
/// <inheritdoc />
public override Task Delete(int id)
public override Task Delete(int id)
=> throw new InvalidOperationException();
/// <inheritdoc />
public override Task Delete(string slug)
public override Task Delete(string slug)
=> throw new InvalidOperationException();
/// <inheritdoc />
public override Task Delete(LibraryItem obj)
public override Task Delete(LibraryItem obj)
=> throw new InvalidOperationException();
/// <summary>
@ -125,8 +125,8 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc />
public async Task<ICollection<LibraryItem>> GetFromLibrary(int id,
Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default,
Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default,
Pagination limit = default)
{
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.ID == id),
@ -137,11 +137,11 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException();
return items;
}
/// <inheritdoc />
public async Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default,
Expression<Func<LibraryItem, bool>> where = null,
Sort<LibraryItem> sort = default,
Pagination limit = default)
{
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
/// </summary>
private readonly IProviderRepository _providers;
/// <inheritdoc />
protected override Expression<Func<Library, object>> DefaultSort => x => x.ID;
@ -41,7 +41,7 @@ namespace Kyoo.Core.Controllers
_providers = providers;
}
/// <inheritdoc />
public override async Task<ICollection<Library>> Search(string query)
{
@ -65,14 +65,14 @@ namespace Kyoo.Core.Controllers
protected override async Task Validate(Library resource)
{
await base.Validate(resource);
if (string.IsNullOrEmpty(resource.Slug))
throw new ArgumentException("The library's slug must be set and not empty");
if (string.IsNullOrEmpty(resource.Name))
throw new ArgumentException("The library's name must be set and not empty");
if (resource.Paths == null || !resource.Paths.Any())
throw new ArgumentException("The library should have a least one path.");
if (resource.Providers != null)
{
resource.Providers = await resource.Providers
@ -99,7 +99,7 @@ namespace Kyoo.Core.Controllers
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted;
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.
/// </summary>
protected abstract Expression<Func<T, object>> DefaultSort { get; }
/// <summary>
/// Create a new base <see cref="LocalRepository{T}"/> with the given database handle.
/// </summary>
@ -57,7 +57,7 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}");
return ret;
}
/// <inheritdoc/>
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.");
return ret;
}
/// <inheritdoc />
public virtual Task<T> GetOrDefault(int id)
{
return Database.Set<T>().FirstOrDefaultAsync(x => x.ID == id);
}
/// <inheritdoc />
public virtual Task<T> GetOrDefault(string slug)
{
return Database.Set<T>().FirstOrDefaultAsync(x => x.Slug == slug);
}
/// <inheritdoc />
public virtual Task<T> GetOrDefault(Expression<Func<T, bool>> where)
{
@ -105,7 +105,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc/>
public abstract Task<ICollection<T>> Search(string query);
/// <inheritdoc/>
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
Sort<T> sort = default,
@ -113,7 +113,7 @@ namespace Kyoo.Core.Controllers
{
return ApplyFilters(Database.Set<T>(), where, sort, limit);
}
/// <summary>
/// Apply filters to a query to ease sort, pagination & where queries for resources of this repository
/// </summary>
@ -124,12 +124,12 @@ namespace Kyoo.Core.Controllers
/// <returns>The filtered query</returns>
protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
Expression<Func<T, bool>> where = null,
Sort<T> sort = default,
Sort<T> sort = default,
Pagination limit = default)
{
return ApplyFilters(query, GetOrDefault, DefaultSort, where, sort, limit);
}
/// <summary>
/// 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"/>
@ -145,17 +145,17 @@ namespace Kyoo.Core.Controllers
Func<int, Task<TValue>> get,
Expression<Func<TValue, object>> defaultSort,
Expression<Func<TValue, bool>> where = null,
Sort<TValue> sort = default,
Sort<TValue> sort = default,
Pagination limit = default)
{
if (where != null)
query = query.Where(where);
Expression<Func<TValue, object>> sortKey = sort.Key ?? defaultSort;
Expression sortExpression = sortKey.Body.NodeType == ExpressionType.Convert
? ((UnaryExpression)sortKey.Body).Operand
: sortKey.Body;
if (typeof(Enum).IsAssignableFrom(sortExpression.Type))
throw new ArgumentException("Invalid sort key.");
@ -205,7 +205,7 @@ namespace Kyoo.Core.Controllers
T old = await GetOrDefault(obj.Slug);
if (old != null)
return old;
return await Create(obj);
}
catch (DuplicatedItemException)
@ -225,7 +225,7 @@ namespace Kyoo.Core.Controllers
try
{
T old = await GetWithTracking(edited.ID);
if (resetOld)
old = Merger.Nullify(old);
Merger.Complete(old, edited, x => x.GetCustomAttribute<LoadableRelationAttribute>() == null);
@ -239,7 +239,7 @@ namespace Kyoo.Core.Controllers
Database.ChangeTracker.Clear();
}
}
/// <summary>
/// An overridable method to edit relation of a resource.
/// </summary>
@ -257,7 +257,7 @@ namespace Kyoo.Core.Controllers
{
return Validate(resource);
}
/// <summary>
/// A method called just before saving a new resource to the database.
/// 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();
if (setter != null)
setter.Invoke(resource, new object[] {resource.Slug + '!'});
setter.Invoke(resource, new object[] { resource.Slug + '!' });
else
throw new ArgumentException("Resources slug can't be number only.");
}
@ -306,7 +306,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc/>
public abstract Task Delete(T obj);
/// <inheritdoc/>
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.
/// </summary>
private readonly Lazy<IShowRepository> _shows;
/// <inheritdoc />
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>
public PeopleRepository(DatabaseContext database,
IProviderRepository providers,
Lazy<IShowRepository> shows)
Lazy<IShowRepository> shows)
: base(database)
{
_database = database;
_providers = providers;
_shows = shows;
}
/// <inheritdoc />
public override async Task<ICollection<People>> Search(string query)
@ -89,7 +89,7 @@ namespace Kyoo.Core.Controllers
{
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);
role.ShowID = role.Show.ID;
_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)
{
await Validate(changed);
if (changed.Roles != null || resetOld)
{
await Database.Entry(resource).Collection(x => x.Roles).LoadAsync();
@ -120,7 +120,7 @@ namespace Kyoo.Core.Controllers
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted;
obj.ExternalIDs.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 />
public async Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
public async Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
@ -151,7 +151,7 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
@ -169,11 +169,11 @@ namespace Kyoo.Core.Controllers
role.ForPeople = true;
return people;
}
/// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromPeople(int id,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
@ -188,11 +188,11 @@ namespace Kyoo.Core.Controllers
throw new ItemNotFoundException();
return roles;
}
/// <inheritdoc />
public async Task<ICollection<PeopleRole>> GetFromPeople(string slug,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
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}");
return ret;
}
/// <inheritdoc/>
public async Task<Season> Get(string showSlug, int seasonNumber)
{
@ -63,14 +63,14 @@ namespace Kyoo.Core.Controllers
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
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);
}
@ -121,14 +121,14 @@ namespace Kyoo.Core.Controllers
protected override async Task EditRelations(Season resource, Season changed, bool resetOld)
{
await Validate(changed);
if (changed.ExternalIDs != null || resetOld)
{
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
resource.ExternalIDs = changed.ExternalIDs;
}
}
/// <inheritdoc/>
public override async Task Delete(Season obj)
{
@ -139,4 +139,4 @@ namespace Kyoo.Core.Controllers
await _database.SaveChangesAsync();
}
}
}
}

View File

@ -50,8 +50,8 @@ namespace Kyoo.Core.Controllers
/// <param name="providers">A provider repository</param>
public ShowRepository(DatabaseContext database,
IStudioRepository studios,
IPeopleRepository people,
IGenreRepository genres,
IPeopleRepository people,
IGenreRepository genres,
IProviderRepository providers)
: base(database)
{
@ -61,7 +61,7 @@ namespace Kyoo.Core.Controllers
_genres = genres;
_providers = providers;
}
/// <inheritdoc />
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).");
return obj;
}
/// <inheritdoc />
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)
{
await Validate(changed);
if (changed.Aliases != null || resetOld)
resource.Aliases = changed.Aliases;
@ -137,7 +137,7 @@ namespace Kyoo.Core.Controllers
await Database.Entry(resource).Reference(x => x.Studio).LoadAsync();
resource.Studio = changed.Studio;
}
if (changed.Genres != null || resetOld)
{
await Database.Entry(resource).Collection(x => x.Genres).LoadAsync();
@ -177,7 +177,7 @@ namespace Kyoo.Core.Controllers
await _database.SaveIfNoDuplicates();
}
}
/// <inheritdoc />
public Task<string> GetSlug(int showID)
{
@ -185,7 +185,7 @@ namespace Kyoo.Core.Controllers
.Select(x => x.Slug)
.FirstOrDefaultAsync();
}
/// <inheritdoc />
public override async Task Delete(Show obj)
{

View File

@ -19,12 +19,12 @@ namespace Kyoo.Core.Controllers
/// The database handle
/// </summary>
private readonly DatabaseContext _database;
/// <summary>
/// A provider repository to handle externalID creation and deletion
/// </summary>
private readonly IProviderRepository _providers;
/// <inheritdoc />
protected override Expression<Func<Studio, object>> DefaultSort => x => x.Name;
@ -40,7 +40,7 @@ namespace Kyoo.Core.Controllers
_database = database;
_providers = providers;
}
/// <inheritdoc />
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).");
return obj;
}
/// <inheritdoc />
protected override async Task Validate(Studio resource)
{
@ -75,7 +75,7 @@ namespace Kyoo.Core.Controllers
_database.MetadataIds<Studio>().AttachRange(resource.ExternalIDs);
}
}
/// <inheritdoc />
protected override async Task EditRelations(Studio resource, Studio changed, bool resetOld)
{
@ -93,7 +93,7 @@ namespace Kyoo.Core.Controllers
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted;
await _database.SaveChangesAsync();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -69,7 +69,7 @@ namespace Kyoo.Core
{
foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(services);
IEnumerable<KeyValuePair<string, Type>> configTypes = _plugins.GetAllPlugins()
.SelectMany(x => x.Configuration)
.Where(x => x.Value != null);
@ -92,7 +92,7 @@ namespace Kyoo.Core
{
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
builder.RegisterTask<PluginInitializer>();
foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(builder);
}
@ -125,8 +125,8 @@ namespace Kyoo.Core
foreach ((string path, Type type) in pluginConfig)
config.Register(path, type);
}
/// <summary>
/// Create a new <see cref="PluginsStartup"/> from a webhost.
/// 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.
/// </param>
/// <returns>A new <see cref="PluginsStartup"/>.</returns>
public static PluginsStartup FromWebHost(WebHostBuilderContext host,
public static PluginsStartup FromWebHost(WebHostBuilderContext host,
ILoggerFactory logger)
{
HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger);
@ -147,7 +147,7 @@ namespace Kyoo.Core
);
return new PluginsStartup(plugins, host.Configuration);
}
/// <summary>
/// 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.
@ -158,18 +158,18 @@ namespace Kyoo.Core
/// The host environment that could be used by plugins to configure themself.
/// </summary>
private readonly IWebHostEnvironment _hostEnvironment;
/// <summary>
/// The configuration context.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// A logger factory used to create a logger for the plugin manager.
/// </summary>
private readonly ILoggerFactory _loggerFactory;
/// <summary>
/// Create a new <see cref="HostServiceProvider"/> that will return given services when asked.
/// </summary>

View File

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

View File

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

View File

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

View File

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

View File

@ -76,7 +76,7 @@ namespace Kyoo.Core.Tasks
TaskParameter.CreateRequired<Library>("library", "The library in witch the episode is")
};
}
/// <inheritdoc />
public async Task Run(TaskParameters arguments, IProgress<float> progress, CancellationToken cancellationToken)
{
@ -148,7 +148,7 @@ namespace Kyoo.Core.Tasks
throw new TaskFailedException(ex);
}
}
/// <summary>
/// Retrieve the equivalent item if it already exists in the database,
/// if it does not, fill metadata using the metadata provider, download images and register the item to the
@ -172,7 +172,7 @@ namespace Kyoo.Core.Tasks
item = await _metadataProvider.Get(item);
await _thumbnailsManager.DownloadImages(item);
switch (item)
{
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")
};
}
/// <inheritdoc />
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 IThumbnailsManager _thumbs;
public CollectionApi(ILibraryManager libraryManager,
IFileSystem files,
public CollectionApi(ILibraryManager libraryManager,
IFileSystem files,
IThumbnailsManager thumbs,
IOptions<BasicOptions> options)
IOptions<BasicOptions> options)
: base(libraryManager.CollectionRepository, options.Value.PublicUrl)
{
_libraryManager = libraryManager;
_files = files;
_thumbs = thumbs;
}
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[PartialPermission(Kind.Read)]
@ -55,10 +55,10 @@ namespace Kyoo.Core.Api
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
return BadRequest(new { Error = ex.Message });
}
}
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[PartialPermission(Kind.Read)]
@ -81,10 +81,10 @@ namespace Kyoo.Core.Api
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
return BadRequest(new { Error = ex.Message });
}
}
[HttpGet("{id:int}/library")]
[HttpGet("{id:int}/libraries")]
[PartialPermission(Kind.Read)]
@ -107,10 +107,10 @@ namespace Kyoo.Core.Api
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
return BadRequest(new { Error = ex.Message });
}
}
[HttpGet("{slug}/library")]
[HttpGet("{slug}/libraries")]
[PartialPermission(Kind.Read)]
@ -133,10 +133,10 @@ namespace Kyoo.Core.Api
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
return BadRequest(new { Error = ex.Message });
}
}
[HttpGet("{slug}/poster")]
public async Task<IActionResult> GetPoster(string slug)
{
@ -150,7 +150,7 @@ namespace Kyoo.Core.Api
return NotFound();
}
}
[HttpGet("{slug}/logo")]
public async Task<IActionResult> GetLogo(string slug)
{
@ -164,7 +164,7 @@ namespace Kyoo.Core.Api
return NotFound();
}
}
[HttpGet("{slug}/backdrop")]
[HttpGet("{slug}/thumbnail")]
public async Task<IActionResult> GetBackdrop(string slug)

View File

@ -25,7 +25,7 @@ namespace Kyoo.Core.Api
public EpisodeApi(ILibraryManager libraryManager,
IOptions<BasicOptions> options,
IFileSystem files,
IThumbnailsManager thumbnails)
IThumbnailsManager thumbnails)
: base(libraryManager.EpisodeRepository, options.Value.PublicUrl)
{
_libraryManager = libraryManager;
@ -37,12 +37,12 @@ namespace Kyoo.Core.Api
[PartialPermission(Kind.Read)]
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)
return NotFound();
return ret;
}
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/show")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber, int episodeNumber)
@ -52,7 +52,7 @@ namespace Kyoo.Core.Api
return NotFound();
return ret;
}
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/show")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber, int episodeNumber)
@ -62,7 +62,7 @@ namespace Kyoo.Core.Api
return NotFound();
return ret;
}
[HttpGet("{episodeID:int}/season")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(int episodeID)
@ -72,7 +72,7 @@ namespace Kyoo.Core.Api
return NotFound();
return ret;
}
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
@ -86,7 +86,7 @@ namespace Kyoo.Core.Api
return NotFound();
}
}
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
[PartialPermission(Kind.Read)]
public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
@ -100,7 +100,7 @@ namespace Kyoo.Core.Api
return NotFound();
}
}
[HttpGet("{episodeID:int}/track")]
[HttpGet("{episodeID:int}/tracks")]
[PartialPermission(Kind.Read)]
@ -123,10 +123,10 @@ namespace Kyoo.Core.Api
}
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}/tracks")]
[PartialPermission(Kind.Read)]
@ -141,7 +141,7 @@ namespace Kyoo.Core.Api
try
{
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.EpisodeNumber == episodeNumber),
new Sort<Track>(sortBy),
@ -153,10 +153,10 @@ namespace Kyoo.Core.Api
}
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}/tracks")]
[PartialPermission(Kind.Read)]
@ -171,7 +171,7 @@ namespace Kyoo.Core.Api
try
{
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.EpisodeNumber == episodeNumber),
new Sort<Track>(sortBy),
@ -183,10 +183,10 @@ namespace Kyoo.Core.Api
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
return BadRequest(new { Error = ex.Message });
}
}
[HttpGet("{id:int}/thumbnail")]
[HttpGet("{id:int}/backdrop")]
public async Task<IActionResult> GetThumb(int id)
@ -201,7 +201,7 @@ namespace Kyoo.Core.Api
return NotFound();
}
}
[HttpGet("{slug}/thumbnail")]
[HttpGet("{slug}/backdrop")]
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;
}
[HttpGet("{id:int}/show")]
[HttpGet("{id:int}/shows")]
[PartialPermission(Kind.Read)]
@ -47,10 +47,10 @@ namespace Kyoo.Core.Api
}
catch (ArgumentException ex)
{
return BadRequest(new {Error = ex.Message});
return BadRequest(new { Error = ex.Message });
}
}
[HttpGet("{slug}/show")]
[HttpGet("{slug}/shows")]
[PartialPermission(Kind.Read)]
@ -73,7 +73,7 @@ namespace Kyoo.Core.Api
}
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);
}
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)
{
if (where == null || where.Count == 0)
@ -35,7 +35,7 @@ namespace Kyoo.Core.Api
{
if (key == null || desired == null)
throw new ArgumentException("Invalid key/value pair. Can't be null.");
string value = desired;
string operand = "eq";
if (desired.Contains(':'))
@ -68,15 +68,15 @@ namespace Kyoo.Core.Api
valueExpr = Expression.Constant(val, property.PropertyType);
}
Expression condition = operand switch
{
"eq" when isList => ContainsResourceExpression(propertyExpr, value),
"ctn" => ContainsResourceExpression(propertyExpr, value),
"eq" when valueExpr == null => ResourceEqual(propertyExpr, value),
"not" when valueExpr == null => ResourceEqual(propertyExpr, value, true),
"eq" => Expression.Equal(propertyExpr, valueExpr),
"not" => Expression.NotEqual(propertyExpr, valueExpr!),
"lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr),
@ -110,11 +110,11 @@ namespace Kyoo.Core.Api
valueConst = Expression.Constant(value);
}
return notEqual
? Expression.NotEqual(field, valueConst)
return notEqual
? Expression.NotEqual(field, valueConst)
: Expression.Equal(field, valueConst);
}
private static Expression ContainsResourceExpression(MemberExpression xProperty, string value)
{
// x => x.PROPERTY.Any(y => y.Slug == value)

View File

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

View File

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