mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-03 05:34:23 -04:00
CodingStyle: Fixing most style issues
This commit is contained in:
parent
ad0235307f
commit
2bb40874b3
@ -14,6 +14,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Kyoo.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)Kyoo.ruleset</CodeAnalysisRuleSet>
|
||||||
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->
|
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->
|
||||||
|
@ -221,7 +221,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// Create a new resource.
|
/// Create a new resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The item to register</param>
|
/// <param name="obj">The item to register</param>
|
||||||
/// <returns>The resource registers and completed by database's information (related items & so on)</returns>
|
/// <returns>The resource registers and completed by database's information (related items and so on)</returns>
|
||||||
[ItemNotNull]
|
[ItemNotNull]
|
||||||
Task<T> Create([NotNull] T obj);
|
Task<T> Create([NotNull] T obj);
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// <param name="edited">The resource to edit, it's ID can't change.</param>
|
/// <param name="edited">The resource to edit, it's ID can't change.</param>
|
||||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
||||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||||
/// <returns>The resource edited and completed by database's information (related items & so on)</returns>
|
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||||
[ItemNotNull]
|
[ItemNotNull]
|
||||||
Task<T> Edit([NotNull] T edited, bool resetOld);
|
Task<T> Edit([NotNull] T edited, bool resetOld);
|
||||||
|
|
||||||
@ -422,7 +422,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ID of the library</param>
|
/// <param name="id">The ID of the library</param>
|
||||||
/// <param name="where">A filter function</param>
|
/// <param name="where">A filter function</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">How many items to return and where to start</param>
|
/// <param name="limit">How many items to return and where to start</param>
|
||||||
/// <returns>A list of items that match every filters</returns>
|
/// <returns>A list of items that match every filters</returns>
|
||||||
public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||||
@ -449,7 +449,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slug">The slug of the library</param>
|
/// <param name="slug">The slug of the library</param>
|
||||||
/// <param name="where">A filter function</param>
|
/// <param name="where">A filter function</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">How many items to return and where to start</param>
|
/// <param name="limit">How many items to return and where to start</param>
|
||||||
/// <returns>A list of items that match every filters</returns>
|
/// <returns>A list of items that match every filters</returns>
|
||||||
public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||||
@ -497,7 +497,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="showID">The ID of the show</param>
|
/// <param name="showID">The ID of the show</param>
|
||||||
/// <param name="where">A filter function</param>
|
/// <param name="where">A filter function</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">How many items to return and where to start</param>
|
/// <param name="limit">How many items to return and where to start</param>
|
||||||
/// <returns>A list of items that match every filters</returns>
|
/// <returns>A list of items that match every filters</returns>
|
||||||
Task<ICollection<PeopleRole>> GetFromShow(int showID,
|
Task<ICollection<PeopleRole>> GetFromShow(int showID,
|
||||||
@ -524,7 +524,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="showSlug">The slug of the show</param>
|
/// <param name="showSlug">The slug of the show</param>
|
||||||
/// <param name="where">A filter function</param>
|
/// <param name="where">A filter function</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">How many items to return and where to start</param>
|
/// <param name="limit">How many items to return and where to start</param>
|
||||||
/// <returns>A list of items that match every filters</returns>
|
/// <returns>A list of items that match every filters</returns>
|
||||||
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
|
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
|
||||||
@ -551,7 +551,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The id of the person</param>
|
/// <param name="id">The id of the person</param>
|
||||||
/// <param name="where">A filter function</param>
|
/// <param name="where">A filter function</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">How many items to return and where to start</param>
|
/// <param name="limit">How many items to return and where to start</param>
|
||||||
/// <returns>A list of items that match every filters</returns>
|
/// <returns>A list of items that match every filters</returns>
|
||||||
Task<ICollection<PeopleRole>> GetFromPeople(int id,
|
Task<ICollection<PeopleRole>> GetFromPeople(int id,
|
||||||
@ -578,7 +578,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slug">The slug of the person</param>
|
/// <param name="slug">The slug of the person</param>
|
||||||
/// <param name="where">A filter function</param>
|
/// <param name="where">A filter function</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">How many items to return and where to start</param>
|
/// <param name="limit">How many items to return and where to start</param>
|
||||||
/// <returns>A list of items that match every filters</returns>
|
/// <returns>A list of items that match every filters</returns>
|
||||||
Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
||||||
@ -610,7 +610,7 @@ namespace Kyoo.Abstractions.Controllers
|
|||||||
/// Get a list of external ids that match all filters
|
/// Get a list of external ids that match all filters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="where">A predicate to add arbitrary filter</param>
|
/// <param name="where">A predicate to add arbitrary filter</param>
|
||||||
/// <param name="sort">Sort information (sort order & sort by)</param>
|
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||||
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
||||||
/// <typeparam name="T">The type of metadata to retrieve</typeparam>
|
/// <typeparam name="T">The type of metadata to retrieve</typeparam>
|
||||||
/// <returns>A filtered list of external ids.</returns>
|
/// <returns>A filtered list of external ids.</returns>
|
||||||
|
30
Kyoo.Abstractions/Models/Resources/WatchedEpisode.cs
Normal file
30
Kyoo.Abstractions/Models/Resources/WatchedEpisode.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Kyoo.Abstractions.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata of episode currently watching by an user
|
||||||
|
/// </summary>
|
||||||
|
public class WatchedEpisode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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>
|
||||||
|
public int WatchedPercentage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -190,13 +190,13 @@ namespace Kyoo.Abstractions.Models
|
|||||||
Subtitles = ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray(),
|
Subtitles = ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray(),
|
||||||
PreviousEpisode = previous,
|
PreviousEpisode = previous,
|
||||||
NextEpisode = next,
|
NextEpisode = next,
|
||||||
Chapters = await GetChapters(ep.Path)
|
Chapters = await _GetChapters(ep.Path)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move this method in a controller to support abstraction.
|
// TODO move this method in a controller to support abstraction.
|
||||||
// TODO use a IFileManager to retrieve and read files.
|
// TODO use a IFileManager to retrieve and read files.
|
||||||
private static async Task<ICollection<Chapter>> GetChapters(string episodePath)
|
private static async Task<ICollection<Chapter>> _GetChapters(string episodePath)
|
||||||
{
|
{
|
||||||
string path = PathIO.Combine(
|
string path = PathIO.Combine(
|
||||||
PathIO.GetDirectoryName(episodePath)!,
|
PathIO.GetDirectoryName(episodePath)!,
|
||||||
|
@ -21,6 +21,7 @@ namespace Kyoo.Utils
|
|||||||
/// <param name="first">The first enumerable to merge</param>
|
/// <param name="first">The first enumerable to merge</param>
|
||||||
/// <param name="second">The second enumerable to merge, if items from this list are equals to one from the first, they are not kept</param>
|
/// <param name="second">The second enumerable to merge, if items from this list are equals to one from the first, they are not kept</param>
|
||||||
/// <param name="isEqual">Equality function to compare items. If this is null, duplicated elements are kept</param>
|
/// <param name="isEqual">Equality function to compare items. If this is null, duplicated elements are kept</param>
|
||||||
|
/// <typeparam name="T">The type of items in the lists to merge.</typeparam>
|
||||||
/// <returns>The two list merged as an array</returns>
|
/// <returns>The two list merged as an array</returns>
|
||||||
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
[ContractAnnotation("first:notnull => notnull; second:notnull => notnull", true)]
|
||||||
public static T[] MergeLists<T>([CanBeNull] IEnumerable<T> first,
|
public static T[] MergeLists<T>([CanBeNull] IEnumerable<T> first,
|
||||||
@ -219,7 +220,8 @@ namespace Kyoo.Utils
|
|||||||
{
|
{
|
||||||
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
||||||
.GenericTypeArguments;
|
.GenericTypeArguments;
|
||||||
object[] parameters = {
|
object[] parameters =
|
||||||
|
{
|
||||||
property.GetValue(first),
|
property.GetValue(first),
|
||||||
value,
|
value,
|
||||||
false
|
false
|
||||||
@ -290,7 +292,8 @@ namespace Kyoo.Utils
|
|||||||
{
|
{
|
||||||
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
Type[] dictionaryTypes = Utility.GetGenericDefinition(property.PropertyType, typeof(IDictionary<,>))
|
||||||
.GenericTypeArguments;
|
.GenericTypeArguments;
|
||||||
object[] parameters = {
|
object[] parameters =
|
||||||
|
{
|
||||||
oldValue,
|
oldValue,
|
||||||
newValue,
|
newValue,
|
||||||
false
|
false
|
||||||
|
@ -26,7 +26,7 @@ namespace Kyoo.Utils
|
|||||||
if (ex == null)
|
if (ex == null)
|
||||||
return false;
|
return false;
|
||||||
return ex.Body is MemberExpression ||
|
return ex.Body is MemberExpression ||
|
||||||
ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression;
|
(ex.Body.NodeType == ExpressionType.Convert && ((UnaryExpression)ex.Body).Operand is MemberExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,7 +51,7 @@ namespace Kyoo.Utils
|
|||||||
/// <param name="member">The member value</param>
|
/// <param name="member">The member value</param>
|
||||||
/// <param name="obj">The owner of this member</param>
|
/// <param name="obj">The owner of this member</param>
|
||||||
/// <returns>The value boxed as an object</returns>
|
/// <returns>The value boxed as an object</returns>
|
||||||
/// <exception cref="ArgumentNullException">if <see cref="member"/> or <see cref="obj"/> is null.</exception>
|
/// <exception cref="ArgumentNullException">if <paramref name="member"/> or <paramref name="obj"/> is null.</exception>
|
||||||
/// <exception cref="ArgumentException">The member is not a field or a property.</exception>
|
/// <exception cref="ArgumentException">The member is not a field or a property.</exception>
|
||||||
public static object GetValue([NotNull] this MemberInfo member, [NotNull] object obj)
|
public static object GetValue([NotNull] this MemberInfo member, [NotNull] object obj)
|
||||||
{
|
{
|
||||||
@ -90,7 +90,7 @@ namespace Kyoo.Utils
|
|||||||
str = stringBuilder.ToString().Normalize(NormalizationForm.FormC);
|
str = stringBuilder.ToString().Normalize(NormalizationForm.FormC);
|
||||||
|
|
||||||
str = Regex.Replace(str, @"\s", "-", RegexOptions.Compiled);
|
str = Regex.Replace(str, @"\s", "-", RegexOptions.Compiled);
|
||||||
str = Regex.Replace(str, @"[^\w\s\p{Pd}]", "", RegexOptions.Compiled);
|
str = Regex.Replace(str, @"[^\w\s\p{Pd}]", string.Empty, RegexOptions.Compiled);
|
||||||
str = str.Trim('-', '_');
|
str = str.Trim('-', '_');
|
||||||
str = Regex.Replace(str, @"([-_]){2,}", "$1", RegexOptions.Compiled);
|
str = Regex.Replace(str, @"([-_]){2,}", "$1", RegexOptions.Compiled);
|
||||||
return str;
|
return str;
|
||||||
@ -113,7 +113,7 @@ namespace Kyoo.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The starting type</param>
|
/// <param name="type">The starting type</param>
|
||||||
/// <returns>A list of types</returns>
|
/// <returns>A list of types</returns>
|
||||||
/// <exception cref="ArgumentNullException"><see cref="type"/> can't be null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="type"/> can't be null</exception>
|
||||||
public static IEnumerable<Type> GetInheritanceTree([NotNull] this Type type)
|
public static IEnumerable<Type> GetInheritanceTree([NotNull] this Type type)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
@ -123,9 +123,9 @@ namespace Kyoo.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if <see cref="obj"/> inherit from a generic type <see cref="genericType"/>.
|
/// Check if <paramref name="obj"/> inherit from a generic type <paramref name="genericType"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">Does this object's type is a <see cref="genericType"/></param>
|
/// <param name="obj">Does this object's type is a <paramref name="genericType"/></param>
|
||||||
/// <param name="genericType">The generic type to check against (Only generic types are supported like typeof(IEnumerable<>).</param>
|
/// <param name="genericType">The generic type to check against (Only generic types are supported like typeof(IEnumerable<>).</param>
|
||||||
/// <returns>True if obj inherit from genericType. False otherwise</returns>
|
/// <returns>True if obj inherit from genericType. False otherwise</returns>
|
||||||
/// <exception cref="ArgumentNullException">obj and genericType can't be null</exception>
|
/// <exception cref="ArgumentNullException">obj and genericType can't be null</exception>
|
||||||
@ -137,7 +137,7 @@ namespace Kyoo.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if <see cref="type"/> inherit from a generic type <see cref="genericType"/>.
|
/// Check if <paramref name="type"/> inherit from a generic type <paramref name="genericType"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to check</param>
|
/// <param name="type">The type to check</param>
|
||||||
/// <param name="genericType">The generic type to check against (Only generic types are supported like typeof(IEnumerable<>).</param>
|
/// <param name="genericType">The generic type to check against (Only generic types are supported like typeof(IEnumerable<>).</param>
|
||||||
@ -160,14 +160,14 @@ namespace Kyoo.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the generic definition of <see cref="genericType"/>.
|
/// Get the generic definition of <paramref name="genericType"/>.
|
||||||
/// For example, calling this function with List<string> and typeof(IEnumerable<>) will return IEnumerable<string>
|
/// For example, calling this function with List<string> and typeof(IEnumerable<>) will return IEnumerable<string>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to check</param>
|
/// <param name="type">The type to check</param>
|
||||||
/// <param name="genericType">The generic type to check against (Only generic types are supported like typeof(IEnumerable<>).</param>
|
/// <param name="genericType">The generic type to check against (Only generic types are supported like typeof(IEnumerable<>).</param>
|
||||||
/// <returns>The generic definition of genericType that type inherit or null if type does not implement the generic type.</returns>
|
/// <returns>The generic definition of genericType that type inherit or null if type does not implement the generic type.</returns>
|
||||||
/// <exception cref="ArgumentNullException"><see cref="type"/> and <see cref="genericType"/> can't be null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="type"/> and <paramref name="genericType"/> can't be null</exception>
|
||||||
/// <exception cref="ArgumentException"><see cref="genericType"/> must be a generic type</exception>
|
/// <exception cref="ArgumentException"><paramref name="genericType"/> must be a generic type</exception>
|
||||||
public static Type GetGenericDefinition([NotNull] Type type, [NotNull] Type genericType)
|
public static Type GetGenericDefinition([NotNull] Type type, [NotNull] Type genericType)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
|
@ -103,9 +103,7 @@ namespace Kyoo.Authentication
|
|||||||
services.AddControllers();
|
services.AddControllers();
|
||||||
|
|
||||||
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
|
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
|
||||||
|
|
||||||
// TODO Check if tokens should be stored.
|
// TODO Check if tokens should be stored.
|
||||||
|
|
||||||
List<Client> clients = new();
|
List<Client> clients = new();
|
||||||
_configuration.GetSection("authentication:clients").Bind(clients);
|
_configuration.GetSection("authentication:clients").Bind(clients);
|
||||||
CertificateOption certificateOptions = new();
|
CertificateOption certificateOptions = new();
|
||||||
|
@ -27,11 +27,11 @@ namespace Kyoo.Authentication
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="builder">The identity server that will be modified.</param>
|
/// <param name="builder">The identity server that will be modified.</param>
|
||||||
/// <param name="options">The certificate options</param>
|
/// <param name="options">The certificate options</param>
|
||||||
/// <returns></returns>
|
/// <returns>The initial builder to allow chain-calls.</returns>
|
||||||
public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder,
|
public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder,
|
||||||
CertificateOption options)
|
CertificateOption options)
|
||||||
{
|
{
|
||||||
X509Certificate2 certificate = GetCertificate(options);
|
X509Certificate2 certificate = _GetCertificate(options);
|
||||||
builder.AddSigningCredential(certificate);
|
builder.AddSigningCredential(certificate);
|
||||||
|
|
||||||
if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow)
|
if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow)
|
||||||
@ -40,10 +40,10 @@ namespace Kyoo.Authentication
|
|||||||
if (File.Exists(options.OldFile))
|
if (File.Exists(options.OldFile))
|
||||||
File.Delete(options.OldFile);
|
File.Delete(options.OldFile);
|
||||||
File.Move(options.File, options.OldFile);
|
File.Move(options.File, options.OldFile);
|
||||||
builder.AddValidationKey(GenerateCertificate(options.File, options.Password));
|
builder.AddValidationKey(_GenerateCertificate(options.File, options.Password));
|
||||||
}
|
}
|
||||||
else if (File.Exists(options.OldFile))
|
else if (File.Exists(options.OldFile))
|
||||||
builder.AddValidationKey(GetExistingCredential(options.OldFile, options.Password));
|
builder.AddValidationKey(_GetExistingCredential(options.OldFile, options.Password));
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,11 +52,11 @@ namespace Kyoo.Authentication
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The certificate options</param>
|
/// <param name="options">The certificate options</param>
|
||||||
/// <returns>A valid certificate</returns>
|
/// <returns>A valid certificate</returns>
|
||||||
private static X509Certificate2 GetCertificate(CertificateOption options)
|
private static X509Certificate2 _GetCertificate(CertificateOption options)
|
||||||
{
|
{
|
||||||
return File.Exists(options.File)
|
return File.Exists(options.File)
|
||||||
? GetExistingCredential(options.File, options.Password)
|
? _GetExistingCredential(options.File, options.Password)
|
||||||
: GenerateCertificate(options.File, options.Password);
|
: _GenerateCertificate(options.File, options.Password);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -65,13 +65,12 @@ namespace Kyoo.Authentication
|
|||||||
/// <param name="file">The path of the certificate</param>
|
/// <param name="file">The path of the certificate</param>
|
||||||
/// <param name="password">The password of the certificate</param>
|
/// <param name="password">The password of the certificate</param>
|
||||||
/// <returns>The loaded certificate</returns>
|
/// <returns>The loaded certificate</returns>
|
||||||
private static X509Certificate2 GetExistingCredential(string file, string password)
|
private static X509Certificate2 _GetExistingCredential(string file, string password)
|
||||||
{
|
{
|
||||||
return new X509Certificate2(file, password,
|
X509KeyStorageFlags storeFlags = X509KeyStorageFlags.MachineKeySet |
|
||||||
X509KeyStorageFlags.MachineKeySet |
|
|
||||||
X509KeyStorageFlags.PersistKeySet |
|
X509KeyStorageFlags.PersistKeySet |
|
||||||
X509KeyStorageFlags.Exportable
|
X509KeyStorageFlags.Exportable;
|
||||||
);
|
return new X509Certificate2(file, password, storeFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -80,7 +79,7 @@ namespace Kyoo.Authentication
|
|||||||
/// <param name="file">The path of the output file</param>
|
/// <param name="file">The path of the output file</param>
|
||||||
/// <param name="password">The password of the new certificate</param>
|
/// <param name="password">The password of the new certificate</param>
|
||||||
/// <returns>The generated certificate</returns>
|
/// <returns>The generated certificate</returns>
|
||||||
private static X509Certificate2 GenerateCertificate(string file, string password)
|
private static X509Certificate2 _GenerateCertificate(string file, string password)
|
||||||
{
|
{
|
||||||
SecureRandom random = new();
|
SecureRandom random = new();
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ namespace Kyoo.Authentication
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of officially supported clients.
|
/// Get the list of officially supported clients.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// You can add custom clients in the settings.json file.
|
/// You can add custom clients in the settings.json file.
|
||||||
@ -48,7 +48,7 @@ namespace Kyoo.Authentication
|
|||||||
RequireConsent = false,
|
RequireConsent = false,
|
||||||
|
|
||||||
AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" },
|
AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" },
|
||||||
RedirectUris = { "/", "/silent.html" },
|
RedirectUris = { "/", "/silent.html" },
|
||||||
PostLogoutRedirectUris = { "/logout" }
|
PostLogoutRedirectUris = { "/logout" }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -89,7 +89,7 @@ namespace Kyoo.Authentication.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stayLogged">Should the user stay logged</param>
|
/// <param name="stayLogged">Should the user stay logged</param>
|
||||||
/// <returns>Authentication properties based on a stay login</returns>
|
/// <returns>Authentication properties based on a stay login</returns>
|
||||||
private static AuthenticationProperties StayLogged(bool stayLogged)
|
private static AuthenticationProperties _StayLogged(bool stayLogged)
|
||||||
{
|
{
|
||||||
if (!stayLogged)
|
if (!stayLogged)
|
||||||
return null;
|
return null;
|
||||||
@ -114,7 +114,7 @@ namespace Kyoo.Authentication.Views
|
|||||||
if (!PasswordUtils.CheckPassword(login.Password, user.Password))
|
if (!PasswordUtils.CheckPassword(login.Password, user.Password))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(login.StayLoggedIn));
|
await HttpContext.SignInAsync(user.ToIdentityUser(), _StayLogged(login.StayLoggedIn));
|
||||||
return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true });
|
return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ namespace Kyoo.Authentication.Views
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await HttpContext.SignInAsync(user.ToIdentityUser(), StayLogged(otac.StayLoggedIn));
|
await HttpContext.SignInAsync(user.ToIdentityUser(), _StayLogged(otac.StayLoggedIn));
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,25 @@ namespace Kyoo.Core.Controllers
|
|||||||
_references = references.ToDictionary(x => x.Path, x => x.Type, StringComparer.OrdinalIgnoreCase);
|
_references = references.ToDictionary(x => x.Path, x => x.Type, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transform the configuration section in nested expando objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config">The section to convert</param>
|
||||||
|
/// <returns>The converted section</returns>
|
||||||
|
private static object _ToUntyped(IConfigurationSection config)
|
||||||
|
{
|
||||||
|
ExpandoObject obj = new();
|
||||||
|
|
||||||
|
foreach (IConfigurationSection section in config.GetChildren())
|
||||||
|
{
|
||||||
|
obj.TryAdd(section.Key, _ToUntyped(section));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj.Any())
|
||||||
|
return config.Value;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void AddTyped<T>(string path)
|
public void AddTyped<T>(string path)
|
||||||
{
|
{
|
||||||
@ -119,8 +138,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
// TODO handle lists and dictionaries.
|
// TODO handle lists and dictionaries.
|
||||||
Type type = _GetType(path);
|
Type type = _GetType(path);
|
||||||
if (typeof(T).IsAssignableFrom(type))
|
if (typeof(T).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
throw new InvalidCastException($"The type {typeof(T).Name} is not valid for " +
|
throw new InvalidCastException($"The type {typeof(T).Name} is not valid for " +
|
||||||
$"a resource of type {type.Name}.");
|
$"a resource of type {type.Name}.");
|
||||||
|
}
|
||||||
return (T)GetValue(path);
|
return (T)GetValue(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,24 +192,5 @@ namespace Kyoo.Core.Controllers
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transform the configuration section in nested expando objects.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="config">The section to convert</param>
|
|
||||||
/// <returns>The converted section</returns>
|
|
||||||
private static object _ToUntyped(IConfigurationSection config)
|
|
||||||
{
|
|
||||||
ExpandoObject obj = new();
|
|
||||||
|
|
||||||
foreach (IConfigurationSection section in config.GetChildren())
|
|
||||||
{
|
|
||||||
obj.TryAdd(section.Key, _ToUntyped(section));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj.Any())
|
|
||||||
return config.Value;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ using System.Threading.Tasks;
|
|||||||
using Autofac.Features.Metadata;
|
using Autofac.Features.Metadata;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@ -75,7 +75,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
usablePath = path;
|
usablePath = path;
|
||||||
Meta<Func<IFileSystem>, FileSystemMetadataAttribute> defaultFs = _fileSystems
|
Meta<Func<IFileSystem>, FileSystemMetadataAttribute> defaultFs = _fileSystems
|
||||||
.SingleOrDefault(x => x.Metadata.Scheme.Contains(""));
|
.SingleOrDefault(x => x.Metadata.Scheme.Contains(string.Empty));
|
||||||
if (defaultFs == null)
|
if (defaultFs == null)
|
||||||
throw new ArgumentException($"No file system registered for the default scheme.");
|
throw new ArgumentException($"No file system registered for the default scheme.");
|
||||||
return defaultFs.Value.Invoke();
|
return defaultFs.Value.Invoke();
|
||||||
|
@ -91,48 +91,48 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
throw new NotSupportedException("Extras can not be stored inside an http filesystem.");
|
throw new NotSupportedException("Extras can not be stored inside an http filesystem.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IActionResult"/> to proxy an http request.
|
|
||||||
/// </summary>
|
|
||||||
// TODO remove this suppress message once the class has been implemented.
|
|
||||||
[SuppressMessage("ReSharper", "NotAccessedField.Local")]
|
|
||||||
public class HttpForwardResult : IActionResult
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The path of the request to forward.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Uri _path;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should the proxied result support ranges requests?
|
/// An <see cref="IActionResult"/> to proxy an http request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly bool _rangeSupport;
|
// TODO remove this suppress message once the class has been implemented.
|
||||||
|
[SuppressMessage("ReSharper", "NotAccessedField.Local", Justification = "Not Implemented Yet.")]
|
||||||
/// <summary>
|
public class HttpForwardResult : IActionResult
|
||||||
/// If not null, override the content type of the resulting request.
|
|
||||||
/// </summary>
|
|
||||||
private readonly string _type;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="HttpForwardResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the request to forward.</param>
|
|
||||||
/// <param name="rangeSupport">Should the proxied result support ranges requests?</param>
|
|
||||||
/// <param name="type">If not null, override the content type of the resulting request.</param>
|
|
||||||
public HttpForwardResult(Uri path, bool rangeSupport, string type = null)
|
|
||||||
{
|
{
|
||||||
_path = path;
|
/// <summary>
|
||||||
_rangeSupport = rangeSupport;
|
/// The path of the request to forward.
|
||||||
_type = type;
|
/// </summary>
|
||||||
}
|
private readonly Uri _path;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public Task ExecuteResultAsync(ActionContext context)
|
/// Should the proxied result support ranges requests?
|
||||||
{
|
/// </summary>
|
||||||
// TODO implement that, example: https://github.com/twitchax/AspNetCore.Proxy/blob/14dd0f212d7abb43ca1bf8c890d5efb95db66acb/src/Core/Extensions/Http.cs#L15
|
private readonly bool _rangeSupport;
|
||||||
throw new NotImplementedException();
|
|
||||||
|
/// <summary>
|
||||||
|
/// If not null, override the content type of the resulting request.
|
||||||
|
/// </summary>
|
||||||
|
private readonly string _type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="HttpForwardResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path of the request to forward.</param>
|
||||||
|
/// <param name="rangeSupport">Should the proxied result support ranges requests?</param>
|
||||||
|
/// <param name="type">If not null, override the content type of the resulting request.</param>
|
||||||
|
public HttpForwardResult(Uri path, bool rangeSupport, string type = null)
|
||||||
|
{
|
||||||
|
_path = path;
|
||||||
|
_rangeSupport = rangeSupport;
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task ExecuteResultAsync(ActionContext context)
|
||||||
|
{
|
||||||
|
// TODO implement that, example: https://github.com/twitchax/AspNetCore.Proxy/blob/14dd0f212d7abb43ca1bf8c890d5efb95db66acb/src/Core/Extensions/Http.cs#L15
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,6 +226,8 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1507:CodeMustNotContainMultipleBlankLinesInARow",
|
[SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1507:CodeMustNotContainMultipleBlankLinesInARow",
|
||||||
Justification = "Separate the code by semantics and simplify the code read.")]
|
Justification = "Separate the code by semantics and simplify the code read.")]
|
||||||
|
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1107:Code should not contain multiple statements on one line",
|
||||||
|
Justification = "Assing IDs and Values in the same line.")]
|
||||||
public Task Load(IResource obj, string memberName, bool force = false)
|
public Task Load(IResource obj, string memberName, bool force = false)
|
||||||
{
|
{
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
|
@ -76,7 +76,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path of the dll</param>
|
/// <param name="path">The path of the dll</param>
|
||||||
/// <returns>The list of dlls in hte assembly</returns>
|
/// <returns>The list of dlls in hte assembly</returns>
|
||||||
private IPlugin[] LoadPlugin(string path)
|
private IPlugin[] _LoadPlugin(string path)
|
||||||
{
|
{
|
||||||
path = Path.GetFullPath(path);
|
path = Path.GetFullPath(path);
|
||||||
try
|
try
|
||||||
@ -106,7 +106,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
_logger.LogTrace("Loading new plugins...");
|
_logger.LogTrace("Loading new plugins...");
|
||||||
string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories);
|
string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories);
|
||||||
_plugins.AddRange(plugins
|
_plugins.AddRange(plugins
|
||||||
.Concat(pluginsPaths.SelectMany(LoadPlugin))
|
.Concat(pluginsPaths.SelectMany(_LoadPlugin))
|
||||||
.Where(x => x.Enabled)
|
.Where(x => x.Enabled)
|
||||||
.GroupBy(x => x.Name)
|
.GroupBy(x => x.Name)
|
||||||
.Select(x => x.First())
|
.Select(x => x.First())
|
||||||
|
@ -19,16 +19,16 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ICollection<IMetadataProvider> _providers;
|
private readonly ICollection<IMetadataProvider> _providers;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of selected providers. If no provider has been selected, this is null.
|
|
||||||
/// </summary>
|
|
||||||
private ICollection<Provider> _selectedProviders;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger used to print errors.
|
/// The logger used to print errors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILogger<ProviderComposite> _logger;
|
private readonly ILogger<ProviderComposite> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of selected providers. If no provider has been selected, this is null.
|
||||||
|
/// </summary>
|
||||||
|
private ICollection<Provider> _selectedProviders;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="ProviderComposite"/> with a list of available providers.
|
/// Create a new <see cref="ProviderComposite"/> with a list of available providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -116,7 +116,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
await base.Create(obj);
|
await base.Create(obj);
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
_database.Entry(obj).State = EntityState.Added;
|
||||||
await _database.SaveChangesAsync($"Trying to insert a duplicated episode (slug {obj.Slug} already exists).");
|
await _database.SaveChangesAsync($"Trying to insert a duplicated episode (slug {obj.Slug} already exists).");
|
||||||
return await ValidateTracks(obj);
|
return await _ValidateTracks(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -128,7 +128,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
await _tracks.DeleteAll(x => x.EpisodeID == resource.ID);
|
await _tracks.DeleteAll(x => x.EpisodeID == resource.ID);
|
||||||
resource.Tracks = changed.Tracks;
|
resource.Tracks = changed.Tracks;
|
||||||
await ValidateTracks(resource);
|
await _ValidateTracks(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed.ExternalIDs != null || resetOld)
|
if (changed.ExternalIDs != null || resetOld)
|
||||||
@ -143,7 +143,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="resource">The resource to fix.</param>
|
/// <param name="resource">The resource to fix.</param>
|
||||||
/// <returns>The <see cref="resource"/> parameter is returned.</returns>
|
/// <returns>The <see cref="resource"/> parameter is returned.</returns>
|
||||||
private async Task<Episode> ValidateTracks(Episode resource)
|
private async Task<Episode> _ValidateTracks(Episode resource)
|
||||||
{
|
{
|
||||||
if (resource.Tracks == null)
|
if (resource.Tracks == null)
|
||||||
return resource;
|
return resource;
|
||||||
@ -165,8 +165,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
if (resource.ShowID <= 0)
|
if (resource.ShowID <= 0)
|
||||||
{
|
{
|
||||||
if (resource.Show == null)
|
if (resource.Show == null)
|
||||||
|
{
|
||||||
throw new ArgumentException($"Can't store an episode not related " +
|
throw new ArgumentException($"Can't store an episode not related " +
|
||||||
$"to any show (showID: {resource.ShowID}).");
|
$"to any show (showID: {resource.ShowID}).");
|
||||||
|
}
|
||||||
resource.ShowID = resource.Show.ID;
|
resource.ShowID = resource.Show.ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="selector">Only items that are part of a library that match this predicate will be returned.</param>
|
/// <param name="selector">Only items that are part of a library that match this predicate will be returned.</param>
|
||||||
/// <returns>A queryable containing items that are part of a library matching the selector.</returns>
|
/// <returns>A queryable containing items that are part of a library matching the selector.</returns>
|
||||||
private IQueryable<LibraryItem> LibraryRelatedQuery(Expression<Func<Library, bool>> selector)
|
private IQueryable<LibraryItem> _LibraryRelatedQuery(Expression<Func<Library, bool>> selector)
|
||||||
=> _database.Libraries
|
=> _database.Libraries
|
||||||
.Where(selector)
|
.Where(selector)
|
||||||
.SelectMany(x => x.Shows)
|
.SelectMany(x => x.Shows)
|
||||||
@ -128,7 +128,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
Sort<LibraryItem> sort = default,
|
Sort<LibraryItem> sort = default,
|
||||||
Pagination limit = default)
|
Pagination limit = default)
|
||||||
{
|
{
|
||||||
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.ID == id),
|
ICollection<LibraryItem> items = await ApplyFilters(_LibraryRelatedQuery(x => x.ID == id),
|
||||||
where,
|
where,
|
||||||
sort,
|
sort,
|
||||||
limit);
|
limit);
|
||||||
@ -143,7 +143,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
Sort<LibraryItem> sort = default,
|
Sort<LibraryItem> sort = default,
|
||||||
Pagination limit = default)
|
Pagination limit = default)
|
||||||
{
|
{
|
||||||
ICollection<LibraryItem> items = await ApplyFilters(LibraryRelatedQuery(x => x.Slug == slug),
|
ICollection<LibraryItem> items = await ApplyFilters(_LibraryRelatedQuery(x => x.Slug == slug),
|
||||||
where,
|
where,
|
||||||
sort,
|
sort,
|
||||||
limit);
|
limit);
|
||||||
|
@ -8,8 +8,8 @@ using Kyoo.Abstractions.Controllers;
|
|||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Utils;
|
|
||||||
using Kyoo.Core.Api;
|
using Kyoo.Core.Api;
|
||||||
|
using Kyoo.Utils;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers
|
namespace Kyoo.Core.Controllers
|
||||||
@ -133,12 +133,13 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// Apply filters to a query to ease sort, pagination & where queries for any resources types.
|
/// Apply filters to a query to ease sort, pagination & where queries for any resources types.
|
||||||
/// For resources of type <see cref="T"/>, see <see cref="ApplyFilters"/>
|
/// For resources of type <see cref="T"/>, see <see cref="ApplyFilters"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The base query to filter.</param>
|
||||||
/// <param name="get">A function to asynchronously get a resource from the database using it's ID.</param>
|
/// <param name="get">A function to asynchronously get a resource from the database using it's ID.</param>
|
||||||
/// <param name="defaultSort">The default sort order of this resource's type.</param>
|
/// <param name="defaultSort">The default sort order of this resource's type.</param>
|
||||||
/// <param name="query">The base query to filter.</param>
|
|
||||||
/// <param name="where">An expression to filter based on arbitrary conditions</param>
|
/// <param name="where">An expression to filter based on arbitrary conditions</param>
|
||||||
/// <param name="sort">The sort settings (sort order & sort by)</param>
|
/// <param name="sort">The sort settings (sort order & sort by)</param>
|
||||||
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
||||||
|
/// <typeparam name="TValue">The type of items to query.</typeparam>
|
||||||
/// <returns>The filtered query</returns>
|
/// <returns>The filtered query</returns>
|
||||||
protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query,
|
protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query,
|
||||||
Func<int, Task<TValue>> get,
|
Func<int, Task<TValue>> get,
|
||||||
@ -252,6 +253,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <param name="resetOld">
|
/// <param name="resetOld">
|
||||||
/// A boolean to indicate if all values of resource should be discarded or not.
|
/// A boolean to indicate if all values of resource should be discarded or not.
|
||||||
/// </param>
|
/// </param>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
protected virtual Task EditRelations(T resource, T changed, bool resetOld)
|
||||||
{
|
{
|
||||||
return Validate(resource);
|
return Validate(resource);
|
||||||
@ -265,6 +267,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <exception cref="ArgumentException">
|
/// <exception cref="ArgumentException">
|
||||||
/// You can throw this if the resource is illegal and should not be saved.
|
/// You can throw this if the resource is illegal and should not be saved.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
protected virtual Task Validate(T resource)
|
protected virtual Task Validate(T resource)
|
||||||
{
|
{
|
||||||
if (typeof(T).GetProperty(nameof(resource.Slug))!.GetCustomAttribute<ComputedAttribute>() != null)
|
if (typeof(T).GetProperty(nameof(resource.Slug))!.GetCustomAttribute<ComputedAttribute>() != null)
|
||||||
|
@ -100,8 +100,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
if (resource.ShowID <= 0)
|
if (resource.ShowID <= 0)
|
||||||
{
|
{
|
||||||
if (resource.Show == null)
|
if (resource.Show == null)
|
||||||
throw new ArgumentException(
|
{
|
||||||
$"Can't store a season not related to any show (showID: {resource.ShowID}).");
|
throw new ArgumentException($"Can't store a season not related to any show " +
|
||||||
|
$"(showID: {resource.ShowID}).");
|
||||||
|
}
|
||||||
resource.ShowID = resource.Show.ID;
|
resource.ShowID = resource.Show.ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ using System.Linq.Expressions;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Utils;
|
|
||||||
using Kyoo.Database;
|
using Kyoo.Database;
|
||||||
|
using Kyoo.Utils;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers
|
namespace Kyoo.Core.Controllers
|
||||||
|
@ -46,8 +46,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
resource.EpisodeID = resource.Episode?.ID ?? 0;
|
resource.EpisodeID = resource.Episode?.ID ?? 0;
|
||||||
if (resource.EpisodeID <= 0)
|
if (resource.EpisodeID <= 0)
|
||||||
|
{
|
||||||
throw new ArgumentException("Can't store a track not related to any episode " +
|
throw new ArgumentException("Can't store a track not related to any episode " +
|
||||||
$"(episodeID: {resource.EpisodeID}).");
|
$"(episodeID: {resource.EpisodeID}).");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,16 +90,16 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Queue<QueuedTask> _queuedTasks = new();
|
private readonly Queue<QueuedTask> _queuedTasks = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The currently running task.
|
|
||||||
/// </summary>
|
|
||||||
private (TaskMetadataAttribute, ITask)? _runningTask;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cancellation token used to cancel the running task when the runner should shutdown.
|
/// The cancellation token used to cancel the running task when the runner should shutdown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly CancellationTokenSource _taskToken = new();
|
private readonly CancellationTokenSource _taskToken = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently running task.
|
||||||
|
/// </summary>
|
||||||
|
private (TaskMetadataAttribute, ITask)? _runningTask;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="TaskManager"/>.
|
/// Create a new <see cref="TaskManager"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -116,7 +116,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
Factory = x.Value,
|
Factory = x.Value,
|
||||||
Metadata = x.Metadata,
|
Metadata = x.Metadata,
|
||||||
ScheduledDate = GetNextTaskDate(x.Metadata.Slug)
|
ScheduledDate = _GetNextTaskDate(x.Metadata.Slug)
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
if (_tasks.Any())
|
if (_tasks.Any())
|
||||||
@ -130,6 +130,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Start the runner in another thread.</remarks>
|
/// <remarks>Start the runner in another thread.</remarks>
|
||||||
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
|
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
public override Task StartAsync(CancellationToken cancellationToken)
|
public override Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Task.Run(() => base.StartAsync(cancellationToken), CancellationToken.None);
|
Task.Run(() => base.StartAsync(cancellationToken), CancellationToken.None);
|
||||||
@ -147,6 +148,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// The runner that will host tasks and run queued tasks.
|
/// The runner that will host tasks and run queued tasks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken">A token to stop the runner</param>
|
/// <param name="cancellationToken">A token to stop the runner</param>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_EnqueueStartupTasks();
|
_EnqueueStartupTasks();
|
||||||
@ -174,7 +176,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Task.Delay(1000, cancellationToken);
|
await Task.Delay(1000, cancellationToken);
|
||||||
QueueScheduledTasks();
|
_QueueScheduledTasks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,8 +219,10 @@ namespace Kyoo.Core.Controllers
|
|||||||
.FirstOrDefault(y => string.Equals(y.Key, x.Name, StringComparison.OrdinalIgnoreCase))
|
.FirstOrDefault(y => string.Equals(y.Key, x.Name, StringComparison.OrdinalIgnoreCase))
|
||||||
.Value;
|
.Value;
|
||||||
if (value == null && x.IsRequired)
|
if (value == null && x.IsRequired)
|
||||||
|
{
|
||||||
throw new ArgumentException($"The argument {x.Name} is required to run " +
|
throw new ArgumentException($"The argument {x.Name} is required to run " +
|
||||||
$"{task.Metadata.Name} but it was not specified.");
|
$"{task.Metadata.Name} but it was not specified.");
|
||||||
|
}
|
||||||
return x.CreateValue(value ?? x.DefaultValue);
|
return x.CreateValue(value ?? x.DefaultValue);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -238,7 +242,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start tasks that are scheduled for start.
|
/// Start tasks that are scheduled for start.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void QueueScheduledTasks()
|
private void _QueueScheduledTasks()
|
||||||
{
|
{
|
||||||
IEnumerable<string> tasksToQueue = _tasks.Where(x => x.ScheduledDate <= DateTime.Now)
|
IEnumerable<string> tasksToQueue = _tasks.Where(x => x.ScheduledDate <= DateTime.Now)
|
||||||
.Select(x => x.Metadata.Slug);
|
.Select(x => x.Metadata.Slug);
|
||||||
@ -280,7 +284,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
Arguments = arguments,
|
Arguments = arguments,
|
||||||
CancellationToken = cancellationToken
|
CancellationToken = cancellationToken
|
||||||
});
|
});
|
||||||
_tasks[index].ScheduledDate = GetNextTaskDate(taskSlug);
|
_tasks[index].ScheduledDate = _GetNextTaskDate(taskSlug);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -300,7 +304,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="taskSlug">The slug of the task</param>
|
/// <param name="taskSlug">The slug of the task</param>
|
||||||
/// <returns>The next date.</returns>
|
/// <returns>The next date.</returns>
|
||||||
private DateTime GetNextTaskDate(string taskSlug)
|
private DateTime _GetNextTaskDate(string taskSlug)
|
||||||
{
|
{
|
||||||
if (_options.CurrentValue.Scheduled.TryGetValue(taskSlug, out TimeSpan delay))
|
if (_options.CurrentValue.Scheduled.TryGetValue(taskSlug, out TimeSpan delay))
|
||||||
return DateTime.Now + delay;
|
return DateTime.Now + delay;
|
||||||
|
@ -134,7 +134,7 @@ namespace Kyoo.Core.Controllers
|
|||||||
{
|
{
|
||||||
transmuxFailed = TranscoderAPI.Transmux(episode.Path, manifest, out playableDuration) != 0;
|
transmuxFailed = TranscoderAPI.Transmux(episode.Path, manifest, out playableDuration) != 0;
|
||||||
}, TaskCreationOptions.LongRunning);
|
}, TaskCreationOptions.LongRunning);
|
||||||
while (playableDuration < 10 || !File.Exists(manifest) && !transmuxFailed)
|
while (playableDuration < 10 || (!File.Exists(manifest) && !transmuxFailed))
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
return transmuxFailed ? null : manifest;
|
return transmuxFailed ? null : manifest;
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,8 @@ namespace Kyoo.Core.Models.Watch
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ImmutableDictionary<string, string> SubtitleExtensions = new Dictionary<string, string>
|
public static readonly ImmutableDictionary<string, string> SubtitleExtensions = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{".ass", "ass"},
|
{ ".ass", "ass" },
|
||||||
{".str", "subrip"}
|
{ ".str", "subrip" }
|
||||||
}.ToImmutableDictionary();
|
}.ToImmutableDictionary();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Kyoo.Abstractions.Models;
|
|
||||||
using System;
|
using System;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
|
||||||
namespace Kyoo.Core.Models.Options
|
namespace Kyoo.Core.Models.Options
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
@ -8,22 +9,22 @@ namespace Kyoo.Core.Models.Watch
|
|||||||
/// The unmanaged stream that the transcoder will return.
|
/// The unmanaged stream that the transcoder will return.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||||
public class Stream
|
public struct Stream
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The title of the stream.
|
/// The title of the stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Title { get; set; }
|
public string Title;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The language of this stream (as a ISO-639-2 language code)
|
/// The language of this stream (as a ISO-639-2 language code)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; }
|
public string Language;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The codec of this stream.
|
/// The codec of this stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Codec { get; set; }
|
public string Codec;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is this stream the default one of it's type?
|
/// Is this stream the default one of it's type?
|
||||||
@ -38,12 +39,12 @@ namespace Kyoo.Core.Models.Watch
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The path of this track.
|
/// The path of this track.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeIgnore] public string Path { get; set; }
|
public string Path;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of this stream.
|
/// The type of this stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializeIgnore] public StreamType Type { get; set; }
|
public StreamType Type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a track from this stream.
|
/// Create a track from this stream.
|
||||||
|
@ -61,6 +61,27 @@ namespace Kyoo.Core
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="PluginsStartup"/> from a webhost.
|
||||||
|
/// This is meant to be used from <see cref="WebHostBuilderExtensions.UseStartup"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">The context of the web host.</param>
|
||||||
|
/// <param name="logger">
|
||||||
|
/// 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,
|
||||||
|
ILoggerFactory logger)
|
||||||
|
{
|
||||||
|
HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger);
|
||||||
|
PluginManager plugins = new(
|
||||||
|
hostProvider,
|
||||||
|
Options.Create(host.Configuration.GetSection(BasicOptions.Path).Get<BasicOptions>()),
|
||||||
|
logger.CreateLogger<PluginManager>()
|
||||||
|
);
|
||||||
|
return new PluginsStartup(plugins, host.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configure the services context via the <see cref="PluginManager"/>.
|
/// Configure the services context via the <see cref="PluginManager"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -126,27 +147,6 @@ namespace Kyoo.Core
|
|||||||
config.Register(path, type);
|
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"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="host">The context of the web host.</param>
|
|
||||||
/// <param name="logger">
|
|
||||||
/// 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,
|
|
||||||
ILoggerFactory logger)
|
|
||||||
{
|
|
||||||
HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger);
|
|
||||||
PluginManager plugins = new(
|
|
||||||
hostProvider,
|
|
||||||
Options.Create(host.Configuration.GetSection(BasicOptions.Path).Get<BasicOptions>()),
|
|
||||||
logger.CreateLogger<PluginManager>()
|
|
||||||
);
|
|
||||||
return new PluginsStartup(plugins, host.Configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A simple host service provider used to activate plugins instance.
|
/// A simple host service provider used to activate plugins instance.
|
||||||
/// The same services as a generic host are available and an <see cref="ILoggerFactory"/> has been added.
|
/// The same services as a generic host are available and an <see cref="ILoggerFactory"/> has been added.
|
||||||
|
@ -89,9 +89,9 @@ namespace Kyoo.Core.Tasks
|
|||||||
IProgress<float> reporter = new Progress<float>(x =>
|
IProgress<float> reporter = new Progress<float>(x =>
|
||||||
{
|
{
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
progress.Report(percent + x / libraries.Count);
|
progress.Report(percent + (x / libraries.Count));
|
||||||
});
|
});
|
||||||
await Scan(library, episodes, tracks, reporter, cancellationToken);
|
await _Scan(library, episodes, tracks, reporter, cancellationToken);
|
||||||
percent += 100f / libraries.Count;
|
percent += 100f / libraries.Count;
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
@ -101,7 +101,7 @@ namespace Kyoo.Core.Tasks
|
|||||||
progress.Report(100);
|
progress.Report(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Scan(Library library,
|
private async Task _Scan(Library library,
|
||||||
IEnumerable<Episode> episodes,
|
IEnumerable<Episode> episodes,
|
||||||
IEnumerable<Track> tracks,
|
IEnumerable<Track> tracks,
|
||||||
IProgress<float> progress,
|
IProgress<float> progress,
|
||||||
@ -131,7 +131,7 @@ namespace Kyoo.Core.Tasks
|
|||||||
IProgress<float> reporter = new Progress<float>(x =>
|
IProgress<float> reporter = new Progress<float>(x =>
|
||||||
{
|
{
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
progress.Report((percent + x / paths.Length - 10) / library.Paths.Length);
|
progress.Report((percent + (x / paths.Length) - 10) / library.Paths.Length);
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (string episodePath in paths)
|
foreach (string episodePath in paths)
|
||||||
@ -153,7 +153,7 @@ namespace Kyoo.Core.Tasks
|
|||||||
reporter = new Progress<float>(x =>
|
reporter = new Progress<float>(x =>
|
||||||
{
|
{
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
progress.Report((90 + (percent + x / subtitles.Length)) / library.Paths.Length);
|
progress.Report((90 + (percent + (x / subtitles.Length))) / library.Paths.Length);
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (string trackPath in subtitles)
|
foreach (string trackPath in subtitles)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -8,6 +7,7 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
|
@ -71,11 +71,11 @@ namespace Kyoo.Core.Api
|
|||||||
|
|
||||||
Expression condition = operand switch
|
Expression condition = operand switch
|
||||||
{
|
{
|
||||||
"eq" when isList => ContainsResourceExpression(propertyExpr, value),
|
"eq" when isList => _ContainsResourceExpression(propertyExpr, value),
|
||||||
"ctn" => ContainsResourceExpression(propertyExpr, value),
|
"ctn" => _ContainsResourceExpression(propertyExpr, value),
|
||||||
|
|
||||||
"eq" when valueExpr == null => ResourceEqual(propertyExpr, value),
|
"eq" when valueExpr == null => _ResourceEqual(propertyExpr, value),
|
||||||
"not" when valueExpr == null => ResourceEqual(propertyExpr, value, true),
|
"not" when valueExpr == null => _ResourceEqual(propertyExpr, value, true),
|
||||||
|
|
||||||
"eq" => Expression.Equal(propertyExpr, valueExpr),
|
"eq" => Expression.Equal(propertyExpr, valueExpr),
|
||||||
"not" => Expression.NotEqual(propertyExpr, valueExpr!),
|
"not" => Expression.NotEqual(propertyExpr, valueExpr!),
|
||||||
@ -95,7 +95,7 @@ namespace Kyoo.Core.Api
|
|||||||
return Expression.Lambda<Func<T, bool>>(expression!, param);
|
return Expression.Lambda<Func<T, bool>>(expression!, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Expression ResourceEqual(Expression parameter, string value, bool notEqual = false)
|
private static Expression _ResourceEqual(Expression parameter, string value, bool notEqual = false)
|
||||||
{
|
{
|
||||||
MemberExpression field;
|
MemberExpression field;
|
||||||
ConstantExpression valueConst;
|
ConstantExpression valueConst;
|
||||||
@ -115,14 +115,14 @@ namespace Kyoo.Core.Api
|
|||||||
: Expression.Equal(field, valueConst);
|
: Expression.Equal(field, valueConst);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Expression ContainsResourceExpression(MemberExpression xProperty, string value)
|
private static Expression _ContainsResourceExpression(MemberExpression xProperty, string value)
|
||||||
{
|
{
|
||||||
// x => x.PROPERTY.Any(y => y.Slug == value)
|
// x => x.PROPERTY.Any(y => y.Slug == value)
|
||||||
Expression ret = null;
|
Expression ret = null;
|
||||||
ParameterExpression y = Expression.Parameter(xProperty.Type.GenericTypeArguments.First(), "y");
|
ParameterExpression y = Expression.Parameter(xProperty.Type.GenericTypeArguments.First(), "y");
|
||||||
foreach (string val in value.Split(','))
|
foreach (string val in value.Split(','))
|
||||||
{
|
{
|
||||||
LambdaExpression lambda = Expression.Lambda(ResourceEqual(y, val), y);
|
LambdaExpression lambda = Expression.Lambda(_ResourceEqual(y, val), y);
|
||||||
Expression iteration = Expression.Call(typeof(Enumerable), "Any", xProperty.Type.GenericTypeArguments,
|
Expression iteration = Expression.Call(typeof(Enumerable), "Any", xProperty.Type.GenericTypeArguments,
|
||||||
xProperty, lambda);
|
xProperty, lambda);
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ namespace Kyoo.Core.Api
|
|||||||
{
|
{
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ResourceView]
|
[ResourceView]
|
||||||
public class CrudApi<T> : ControllerBase where T : class, IResource
|
public class CrudApi<T> : ControllerBase
|
||||||
|
where T : class, IResource
|
||||||
{
|
{
|
||||||
private readonly IRepository<T> _repository;
|
private readonly IRepository<T> _repository;
|
||||||
protected readonly Uri BaseURL;
|
protected readonly Uri BaseURL;
|
||||||
|
@ -77,11 +77,11 @@ namespace Kyoo.Core.Api
|
|||||||
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
{
|
{
|
||||||
if (context.Result is ObjectResult result)
|
if (context.Result is ObjectResult result)
|
||||||
await LoadResultRelations(context, result);
|
await _LoadResultRelations(context, result);
|
||||||
await base.OnResultExecutionAsync(context, next);
|
await base.OnResultExecutionAsync(context, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LoadResultRelations(ActionContext context, ObjectResult result)
|
private static async Task _LoadResultRelations(ActionContext context, ObjectResult result)
|
||||||
{
|
{
|
||||||
if (result.DeclaredType == null)
|
if (result.DeclaredType == null)
|
||||||
return;
|
return;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -33,6 +33,7 @@ namespace Kyoo.Core.Api
|
|||||||
if (relation.RelationID == null)
|
if (relation.RelationID == null)
|
||||||
property.ShouldSerialize = x => _depth == 0 && member.GetValue(x) != null;
|
property.ShouldSerialize = x => _depth == 0 && member.GetValue(x) != null;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
property.ShouldSerialize = x =>
|
property.ShouldSerialize = x =>
|
||||||
{
|
{
|
||||||
if (_depth != 0)
|
if (_depth != 0)
|
||||||
@ -41,6 +42,7 @@ namespace Kyoo.Core.Api
|
|||||||
return true;
|
return true;
|
||||||
return x.GetType().GetProperty(relation.RelationID)?.GetValue(x) != null;
|
return x.GetType().GetProperty(relation.RelationID)?.GetValue(x) != null;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member.GetCustomAttribute<SerializeIgnoreAttribute>() != null)
|
if (member.GetCustomAttribute<SerializeIgnoreAttribute>() != null)
|
||||||
@ -70,87 +72,4 @@ namespace Kyoo.Core.Api
|
|||||||
return contract;
|
return contract;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PeopleRoleConverter : JsonConverter<PeopleRole>
|
|
||||||
{
|
|
||||||
public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
ICollection<PeopleRole> oldPeople = value.Show?.People;
|
|
||||||
ICollection<PeopleRole> oldRoles = value.People?.Roles;
|
|
||||||
if (value.Show != null)
|
|
||||||
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);
|
|
||||||
obj.WriteTo(writer);
|
|
||||||
|
|
||||||
if (value.Show != null)
|
|
||||||
value.Show.People = oldPeople;
|
|
||||||
if (value.People != null)
|
|
||||||
value.People.Roles = oldRoles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override PeopleRole ReadJson(JsonReader reader,
|
|
||||||
Type objectType,
|
|
||||||
PeopleRole existingValue,
|
|
||||||
bool hasExistingValue,
|
|
||||||
JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerializeAsProvider : IValueProvider
|
|
||||||
{
|
|
||||||
private string _format;
|
|
||||||
private string _host;
|
|
||||||
|
|
||||||
public SerializeAsProvider(string format, string host)
|
|
||||||
{
|
|
||||||
_format = format;
|
|
||||||
_host = host.TrimEnd('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
public object GetValue(object target)
|
|
||||||
{
|
|
||||||
return Regex.Replace(_format, @"(?<!{){(\w+)(:(\w+))?}", x =>
|
|
||||||
{
|
|
||||||
string value = x.Groups[1].Value;
|
|
||||||
string modifier = x.Groups[3].Value;
|
|
||||||
|
|
||||||
if (value == "HOST")
|
|
||||||
return _host;
|
|
||||||
|
|
||||||
PropertyInfo properties = target.GetType()
|
|
||||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
|
||||||
.FirstOrDefault(y => y.Name == value);
|
|
||||||
if (properties == null)
|
|
||||||
return null;
|
|
||||||
object objValue = properties.GetValue(target);
|
|
||||||
if (objValue is not string ret)
|
|
||||||
ret = objValue?.ToString();
|
|
||||||
if (ret == null)
|
|
||||||
throw new ArgumentException($"Invalid serializer replacement {value}");
|
|
||||||
|
|
||||||
foreach (char modification in modifier)
|
|
||||||
{
|
|
||||||
ret = modification switch
|
|
||||||
{
|
|
||||||
'l' => ret.ToLowerInvariant(),
|
|
||||||
'u' => ret.ToUpperInvariant(),
|
|
||||||
_ => throw new ArgumentException($"Invalid serializer modificator {modification}.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetValue(object target, object value)
|
|
||||||
{
|
|
||||||
// Values are ignored and should not be editable, except if the internal value is set.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
48
Kyoo.Core/Views/Helper/Serializers/PeopleRoleConverter.cs
Normal file
48
Kyoo.Core/Views/Helper/Serializers/PeopleRoleConverter.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
using Kyoo.Utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Api
|
||||||
|
{
|
||||||
|
|
||||||
|
public class PeopleRoleConverter : JsonConverter<PeopleRole>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
ICollection<PeopleRole> oldPeople = value.Show?.People;
|
||||||
|
ICollection<PeopleRole> oldRoles = value.People?.Roles;
|
||||||
|
if (value.Show != null)
|
||||||
|
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);
|
||||||
|
obj.WriteTo(writer);
|
||||||
|
|
||||||
|
if (value.Show != null)
|
||||||
|
value.Show.People = oldPeople;
|
||||||
|
if (value.People != null)
|
||||||
|
value.People.Roles = oldRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PeopleRole ReadJson(JsonReader reader,
|
||||||
|
Type objectType,
|
||||||
|
PeopleRole existingValue,
|
||||||
|
bool hasExistingValue,
|
||||||
|
JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
Kyoo.Core/Views/Helper/Serializers/SerializeAsProvider.cs
Normal file
67
Kyoo.Core/Views/Helper/Serializers/SerializeAsProvider.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
using Kyoo.Utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Api
|
||||||
|
{
|
||||||
|
|
||||||
|
public class SerializeAsProvider : IValueProvider
|
||||||
|
{
|
||||||
|
private string _format;
|
||||||
|
private string _host;
|
||||||
|
|
||||||
|
public SerializeAsProvider(string format, string host)
|
||||||
|
{
|
||||||
|
_format = format;
|
||||||
|
_host = host.TrimEnd('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetValue(object target)
|
||||||
|
{
|
||||||
|
return Regex.Replace(_format, @"(?<!{){(\w+)(:(\w+))?}", x =>
|
||||||
|
{
|
||||||
|
string value = x.Groups[1].Value;
|
||||||
|
string modifier = x.Groups[3].Value;
|
||||||
|
|
||||||
|
if (value == "HOST")
|
||||||
|
return _host;
|
||||||
|
|
||||||
|
PropertyInfo properties = target.GetType()
|
||||||
|
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
.FirstOrDefault(y => y.Name == value);
|
||||||
|
if (properties == null)
|
||||||
|
return null;
|
||||||
|
object objValue = properties.GetValue(target);
|
||||||
|
if (objValue is not string ret)
|
||||||
|
ret = objValue?.ToString();
|
||||||
|
if (ret == null)
|
||||||
|
throw new ArgumentException($"Invalid serializer replacement {value}");
|
||||||
|
|
||||||
|
foreach (char modification in modifier)
|
||||||
|
{
|
||||||
|
ret = modification switch
|
||||||
|
{
|
||||||
|
'l' => ret.ToLowerInvariant(),
|
||||||
|
'u' => ret.ToUpperInvariant(),
|
||||||
|
_ => throw new ArgumentException($"Invalid serializer modificator {modification}.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(object target, object value)
|
||||||
|
{
|
||||||
|
// Values are ignored and should not be editable, except if the internal value is set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -32,9 +32,11 @@ namespace Kyoo.Core.Api
|
|||||||
{
|
{
|
||||||
ActionResult<Library> result = await base.Create(resource);
|
ActionResult<Library> result = await base.Create(resource);
|
||||||
if (result.Value != null)
|
if (result.Value != null)
|
||||||
|
{
|
||||||
_taskManager.StartTask("scan",
|
_taskManager.StartTask("scan",
|
||||||
new Progress<float>(),
|
new Progress<float>(),
|
||||||
new Dictionary<string, object> { { "slug", result.Value.Slug } });
|
new Dictionary<string, object> { { "slug", result.Value.Slug } });
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -9,6 +8,7 @@ using Kyoo.Abstractions.Models;
|
|||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -6,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
@ -64,78 +64,78 @@ namespace Kyoo.Core.Api
|
|||||||
return new ConvertSubripToVtt(subtitle.Path, _files);
|
return new ConvertSubripToVtt(subtitle.Path, _files);
|
||||||
return _files.FileResult(subtitle.Path);
|
return _files.FileResult(subtitle.Path);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public class ConvertSubripToVtt : IActionResult
|
public class ConvertSubripToVtt : IActionResult
|
||||||
{
|
|
||||||
private readonly string _path;
|
|
||||||
private readonly IFileSystem _files;
|
|
||||||
|
|
||||||
public ConvertSubripToVtt(string subtitlePath, IFileSystem files)
|
|
||||||
{
|
{
|
||||||
_path = subtitlePath;
|
private readonly string _path;
|
||||||
_files = files;
|
private readonly IFileSystem _files;
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ExecuteResultAsync(ActionContext context)
|
public ConvertSubripToVtt(string subtitlePath, IFileSystem files)
|
||||||
{
|
|
||||||
List<string> lines = new();
|
|
||||||
|
|
||||||
context.HttpContext.Response.StatusCode = 200;
|
|
||||||
context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt");
|
|
||||||
|
|
||||||
await using (StreamWriter writer = new(context.HttpContext.Response.Body))
|
|
||||||
{
|
{
|
||||||
await writer.WriteLineAsync("WEBVTT");
|
_path = subtitlePath;
|
||||||
await writer.WriteLineAsync("");
|
_files = files;
|
||||||
await writer.WriteLineAsync("");
|
}
|
||||||
|
|
||||||
using StreamReader reader = new(await _files.GetReader(_path));
|
public async Task ExecuteResultAsync(ActionContext context)
|
||||||
string line;
|
{
|
||||||
while ((line = await reader.ReadLineAsync()) != null)
|
List<string> lines = new();
|
||||||
|
|
||||||
|
context.HttpContext.Response.StatusCode = 200;
|
||||||
|
context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt");
|
||||||
|
|
||||||
|
await using (StreamWriter writer = new(context.HttpContext.Response.Body))
|
||||||
{
|
{
|
||||||
if (line == "")
|
await writer.WriteLineAsync("WEBVTT");
|
||||||
|
await writer.WriteLineAsync(string.Empty);
|
||||||
|
await writer.WriteLineAsync(string.Empty);
|
||||||
|
|
||||||
|
using StreamReader reader = new(await _files.GetReader(_path));
|
||||||
|
string line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null)
|
||||||
{
|
{
|
||||||
lines.Add("");
|
if (line == string.Empty)
|
||||||
IEnumerable<string> processedBlock = ConvertBlock(lines);
|
{
|
||||||
foreach (string t in processedBlock)
|
lines.Add(string.Empty);
|
||||||
await writer.WriteLineAsync(t);
|
IEnumerable<string> processedBlock = _ConvertBlock(lines);
|
||||||
lines.Clear();
|
foreach (string t in processedBlock)
|
||||||
|
await writer.WriteLineAsync(t);
|
||||||
|
lines.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lines.Add(line);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
lines.Add(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await context.HttpContext.Response.Body.FlushAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await context.HttpContext.Response.Body.FlushAsync();
|
private static IEnumerable<string> _ConvertBlock(IList<string> lines)
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<string> ConvertBlock(IList<string> lines)
|
|
||||||
{
|
|
||||||
if (lines.Count < 3)
|
|
||||||
return lines;
|
|
||||||
lines[1] = lines[1].Replace(',', '.');
|
|
||||||
if (lines[2].Length > 5)
|
|
||||||
{
|
{
|
||||||
lines[1] += lines[2].Substring(0, 6) switch
|
if (lines.Count < 3)
|
||||||
|
return lines;
|
||||||
|
lines[1] = lines[1].Replace(',', '.');
|
||||||
|
if (lines[2].Length > 5)
|
||||||
{
|
{
|
||||||
"{\\an1}" => " line:93% position:15%",
|
lines[1] += lines[2].Substring(0, 6) switch
|
||||||
"{\\an2}" => " line:93%",
|
{
|
||||||
"{\\an3}" => " line:93% position:85%",
|
"{\\an1}" => " line:93% position:15%",
|
||||||
"{\\an4}" => " line:50% position:15%",
|
"{\\an2}" => " line:93%",
|
||||||
"{\\an5}" => " line:50%",
|
"{\\an3}" => " line:93% position:85%",
|
||||||
"{\\an6}" => " line:50% position:85%",
|
"{\\an4}" => " line:50% position:15%",
|
||||||
"{\\an7}" => " line:7% position:15%",
|
"{\\an5}" => " line:50%",
|
||||||
"{\\an8}" => " line:7%",
|
"{\\an6}" => " line:50% position:85%",
|
||||||
"{\\an9}" => " line:7% position:85%",
|
"{\\an7}" => " line:7% position:15%",
|
||||||
_ => " line:93%"
|
"{\\an8}" => " line:7%",
|
||||||
};
|
"{\\an9}" => " line:7% position:85%",
|
||||||
|
_ => " line:93%"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines[2].StartsWith("{\\an"))
|
||||||
|
lines[2] = lines[2].Substring(6);
|
||||||
|
|
||||||
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lines[2].StartsWith("{\\an"))
|
|
||||||
lines[2] = lines[2].Substring(6);
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ namespace Kyoo.Database
|
|||||||
/// <param name="second">The ID of the second resource.</param>
|
/// <param name="second">The ID of the second resource.</param>
|
||||||
/// <typeparam name="T1">The first resource type of the relation. It is the owner of the second</typeparam>
|
/// <typeparam name="T1">The first resource type of the relation. It is the owner of the second</typeparam>
|
||||||
/// <typeparam name="T2">The second resource type of the relation. It is the contained resource.</typeparam>
|
/// <typeparam name="T2">The second resource type of the relation. It is the contained resource.</typeparam>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
public async Task AddLinks<T1, T2>(int first, int second)
|
public async Task AddLinks<T1, T2>(int first, int second)
|
||||||
where T1 : class, IResource
|
where T1 : class, IResource
|
||||||
where T2 : class, IResource
|
where T2 : class, IResource
|
||||||
@ -433,7 +434,7 @@ namespace Kyoo.Database
|
|||||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
/// <returns>The number of state entries written to the database.</returns>
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
||||||
CancellationToken cancellationToken = new())
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -454,7 +455,7 @@ namespace Kyoo.Database
|
|||||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
/// <returns>The number of state entries written to the database.</returns>
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new())
|
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -538,7 +539,7 @@ namespace Kyoo.Database
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete every changes that are on this context.
|
/// Delete every changes that are on this context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DiscardChanges()
|
public void DiscardChanges()
|
||||||
{
|
{
|
||||||
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Detached))
|
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Detached))
|
||||||
{
|
{
|
||||||
|
@ -166,8 +166,8 @@ namespace Kyoo.SqLite
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool IsDuplicateException(Exception ex)
|
protected override bool IsDuplicateException(Exception ex)
|
||||||
{
|
{
|
||||||
return ex.InnerException is SqliteException { SqliteExtendedErrorCode: 2067 /*SQLITE_CONSTRAINT_UNIQUE*/}
|
return ex.InnerException is SqliteException { SqliteExtendedErrorCode: 2067 /* SQLITE_CONSTRAINT_UNIQUE */ }
|
||||||
or SqliteException { SqliteExtendedErrorCode: 1555 /*SQLITE_CONSTRAINT_PRIMARYKEY*/};
|
or SqliteException { SqliteExtendedErrorCode: 1555 /* SQLITE_CONSTRAINT_PRIMARYKEY */ };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -47,8 +47,10 @@ namespace Kyoo.TheTvdb
|
|||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
|
{
|
||||||
logger.LogWarning("No API key configured for TVDB provider. " +
|
logger.LogWarning("No API key configured for TVDB provider. " +
|
||||||
"To enable TVDB, specify one in the setting TVDB:APIKEY ");
|
"To enable TVDB, specify one in the setting TVDB:APIKEY ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<Rule Id="SA1309" Action="None" /> <!-- FieldNamesMustNotBeginWithUnderscore -->
|
<Rule Id="SA1309" Action="None" /> <!-- FieldNamesMustNotBeginWithUnderscore -->
|
||||||
<Rule Id="SX1309" Action="Warning" /> <!-- FieldNamesMustBeginWithUnderscore -->
|
<Rule Id="SX1309" Action="Warning" /> <!-- FieldNamesMustBeginWithUnderscore -->
|
||||||
<Rule Id="SA1300" Action="None" /> <!-- ElementMustBeginWithUpperCaseLetter (this conflict with the _ prefix for privates, enforced by an IDE rule) -->
|
<Rule Id="SA1300" Action="None" /> <!-- ElementMustBeginWithUpperCaseLetter (this conflict with the _ prefix for privates, enforced by an IDE rule) -->
|
||||||
|
<Rule Id="SA1316" Action="None" /> <!-- TupleElementNamesShouldUseCorrectCasing (should be camels when deconstructing but pascal otherwise. -->
|
||||||
</Rules>
|
</Rules>
|
||||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.ReadabilityRules">
|
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.ReadabilityRules">
|
||||||
<Rule Id="SA1101" Action="None" /> <!-- PrefixLocalCallsWithThis -->
|
<Rule Id="SA1101" Action="None" /> <!-- PrefixLocalCallsWithThis -->
|
||||||
@ -40,5 +41,6 @@
|
|||||||
|
|
||||||
<Rule Id="SA1633" Action="None" /> <!-- FileMustHaveHeader TODO remove this, this is only temporary -->
|
<Rule Id="SA1633" Action="None" /> <!-- FileMustHaveHeader TODO remove this, this is only temporary -->
|
||||||
<Rule Id="SA1629" Action="None" /> <!-- DocumentationTextMustEndWithAPeriod TODO remove this, this is only temporary -->
|
<Rule Id="SA1629" Action="None" /> <!-- DocumentationTextMustEndWithAPeriod TODO remove this, this is only temporary -->
|
||||||
|
<Rule Id="SA1600" Action="None" /> <!-- ElementsMustBeDocumented TODO remove this, this is only temporary -->
|
||||||
</Rules>
|
</Rules>
|
||||||
</RuleSet>
|
</RuleSet>
|
||||||
|
@ -44,7 +44,7 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task CreateWithEmptySlugTest()
|
public async Task CreateWithEmptySlugTest()
|
||||||
{
|
{
|
||||||
Collection collection = TestSample.GetNew<Collection>();
|
Collection collection = TestSample.GetNew<Collection>();
|
||||||
collection.Slug = "";
|
collection.Slug = string.Empty;
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(collection));
|
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(collection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ namespace Kyoo.Tests.Database
|
|||||||
public async Task CreateWithEmptySlugTest()
|
public async Task CreateWithEmptySlugTest()
|
||||||
{
|
{
|
||||||
Library library = TestSample.GetNew<Library>();
|
Library library = TestSample.GetNew<Library>();
|
||||||
library.Slug = "";
|
library.Slug = string.Empty;
|
||||||
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(library));
|
await Assert.ThrowsAsync<ArgumentException>(() => _repository.Create(library));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace Kyoo.Tests.Identifier
|
|||||||
Genres = new[] { new Genre("genre") }
|
Genres = new[] { new Genre("genre") }
|
||||||
};
|
};
|
||||||
Mock<IMetadataProvider> mock = new();
|
Mock<IMetadataProvider> mock = new();
|
||||||
mock.Setup(x => x.Provider).Returns(new Provider("mock", ""));
|
mock.Setup(x => x.Provider).Returns(new Provider("mock", string.Empty));
|
||||||
mock.Setup(x => x.Get(show)).ReturnsAsync(new Show
|
mock.Setup(x => x.Get(show)).ReturnsAsync(new Show
|
||||||
{
|
{
|
||||||
Title = "title",
|
Title = "title",
|
||||||
@ -93,7 +93,7 @@ namespace Kyoo.Tests.Identifier
|
|||||||
});
|
});
|
||||||
|
|
||||||
Mock<IMetadataProvider> mockTwo = new();
|
Mock<IMetadataProvider> mockTwo = new();
|
||||||
mockTwo.Setup(x => x.Provider).Returns(new Provider("mockTwo", ""));
|
mockTwo.Setup(x => x.Provider).Returns(new Provider("mockTwo", string.Empty));
|
||||||
mockTwo.Setup(x => x.Get(show)).ReturnsAsync(new Show
|
mockTwo.Setup(x => x.Get(show)).ReturnsAsync(new Show
|
||||||
{
|
{
|
||||||
Title = "title2",
|
Title = "title2",
|
||||||
@ -102,7 +102,7 @@ namespace Kyoo.Tests.Identifier
|
|||||||
});
|
});
|
||||||
|
|
||||||
Mock<IMetadataProvider> mockFailing = new();
|
Mock<IMetadataProvider> mockFailing = new();
|
||||||
mockFailing.Setup(x => x.Provider).Returns(new Provider("mockFail", ""));
|
mockFailing.Setup(x => x.Provider).Returns(new Provider("mockFail", string.Empty));
|
||||||
mockFailing.Setup(x => x.Get(show)).Throws<ArgumentException>();
|
mockFailing.Setup(x => x.Get(show)).Throws<ArgumentException>();
|
||||||
|
|
||||||
AProviderComposite provider = new ProviderComposite(new[]
|
AProviderComposite provider = new ProviderComposite(new[]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user