mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Removing usless calls, cleaning track's slugs, adding a track api, cleaning show & episode repository
This commit is contained in:
parent
f028d369b9
commit
457dcd0db2
@ -30,7 +30,7 @@ namespace Kyoo.Controllers
|
|||||||
|
|
||||||
public Sort(Expression<Func<T, object>> key, bool descendant = false)
|
public Sort(Expression<Func<T, object>> key, bool descendant = false)
|
||||||
{
|
{
|
||||||
Key = ExpressionRewrite.Rewrite<Func<T, object>>(key);
|
Key = key;
|
||||||
Descendant = descendant;
|
Descendant = descendant;
|
||||||
|
|
||||||
if (!Utility.IsPropertyExpression(Key))
|
if (!Utility.IsPropertyExpression(Key))
|
||||||
@ -54,8 +54,7 @@ namespace Kyoo.Controllers
|
|||||||
Key = property.Type.IsValueType
|
Key = property.Type.IsValueType
|
||||||
? Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param)
|
? Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param)
|
||||||
: Expression.Lambda<Func<T, object>>(property, param);
|
: Expression.Lambda<Func<T, object>>(property, param);
|
||||||
Key = ExpressionRewrite.Rewrite<Func<T, object>>(Key);
|
|
||||||
|
|
||||||
Descendant = order switch
|
Descendant = order switch
|
||||||
{
|
{
|
||||||
"desc" => true,
|
"desc" => true,
|
||||||
@ -64,11 +63,6 @@ namespace Kyoo.Controllers
|
|||||||
_ => throw new ArgumentException($"The sort order, if set, should be :asc or :desc but it was :{order}.")
|
_ => throw new ArgumentException($"The sort order, if set, should be :asc or :desc but it was :{order}.")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sort<TValue> To<TValue>()
|
|
||||||
{
|
|
||||||
return new(Key.Convert<Func<TValue, object>>(), Descendant);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRepository<T> : IDisposable, IAsyncDisposable where T : class, IResource
|
public interface IRepository<T> : IDisposable, IAsyncDisposable where T : class, IResource
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Kyoo
|
|
||||||
{
|
|
||||||
public class ExpressionRewriteAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string Link { get; }
|
|
||||||
public string Inner { get; }
|
|
||||||
|
|
||||||
public ExpressionRewriteAttribute(string link, string inner = null)
|
|
||||||
{
|
|
||||||
Link = link;
|
|
||||||
Inner = inner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ExpressionRewrite : ExpressionVisitor
|
|
||||||
{
|
|
||||||
private string _inner;
|
|
||||||
private readonly List<(string inner, ParameterExpression param, ParameterExpression newParam)> _innerRewrites;
|
|
||||||
|
|
||||||
private ExpressionRewrite()
|
|
||||||
{
|
|
||||||
_innerRewrites = new List<(string, ParameterExpression, ParameterExpression)>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Expression Rewrite(Expression expression)
|
|
||||||
{
|
|
||||||
return new ExpressionRewrite().Visit(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Expression<T> Rewrite<T>(Expression expression) where T : Delegate
|
|
||||||
{
|
|
||||||
return (Expression<T>)new ExpressionRewrite().Visit(expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Expression VisitMember(MemberExpression node)
|
|
||||||
{
|
|
||||||
(string inner, _, ParameterExpression p) = _innerRewrites.FirstOrDefault(x => x.param == node.Expression);
|
|
||||||
if (inner != null)
|
|
||||||
{
|
|
||||||
Expression param = inner.Split('.').Aggregate<string, Expression>(p, Expression.Property);
|
|
||||||
node = Expression.Property(param, node.Member.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't use node.Member directly because we want to support attribute override
|
|
||||||
MemberInfo member = node.Expression!.Type.GetProperty(node.Member.Name) ?? node.Member;
|
|
||||||
ExpressionRewriteAttribute attr = member!.GetCustomAttribute<ExpressionRewriteAttribute>();
|
|
||||||
if (attr == null)
|
|
||||||
return base.VisitMember(node);
|
|
||||||
|
|
||||||
Expression property = attr.Link.Split('.').Aggregate(node.Expression, Expression.Property);
|
|
||||||
if (property is MemberExpression expr)
|
|
||||||
Visit(expr.Expression);
|
|
||||||
_inner = attr.Inner;
|
|
||||||
return property!;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Expression VisitLambda<T>(Expression<T> node)
|
|
||||||
{
|
|
||||||
(_, ParameterExpression oldParam, ParameterExpression param) = _innerRewrites
|
|
||||||
.FirstOrDefault(x => node.Parameters.Any(y => y == x.param));
|
|
||||||
if (param == null)
|
|
||||||
return base.VisitLambda(node);
|
|
||||||
|
|
||||||
ParameterExpression[] newParams = node.Parameters.Where(x => x != oldParam).Append(param).ToArray();
|
|
||||||
return Expression.Lambda(Visit(node.Body)!, newParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Expression VisitMethodCall(MethodCallExpression node)
|
|
||||||
{
|
|
||||||
int count = node.Arguments.Count;
|
|
||||||
if (node.Object != null)
|
|
||||||
count++;
|
|
||||||
if (count != 2)
|
|
||||||
return base.VisitMethodCall(node);
|
|
||||||
|
|
||||||
Expression instance = node.Object ?? node.Arguments.First();
|
|
||||||
Expression argument = node.Object != null
|
|
||||||
? node.Arguments.First()
|
|
||||||
: node.Arguments[1];
|
|
||||||
|
|
||||||
Type oldType = instance.Type;
|
|
||||||
instance = Visit(instance);
|
|
||||||
if (instance!.Type == oldType)
|
|
||||||
return base.VisitMethodCall(node);
|
|
||||||
|
|
||||||
if (_inner != null && argument is LambdaExpression lambda)
|
|
||||||
{
|
|
||||||
// TODO this type handler will usually work with IEnumerable & others but won't work with everything.
|
|
||||||
Type type = oldType.GetGenericArguments().First();
|
|
||||||
ParameterExpression oldParam = lambda.Parameters.FirstOrDefault(x => x.Type == type);
|
|
||||||
if (oldParam != null)
|
|
||||||
{
|
|
||||||
Type newType = instance.Type.GetGenericArguments().First();
|
|
||||||
ParameterExpression newParam = Expression.Parameter(newType, oldParam.Name);
|
|
||||||
_innerRewrites.Add((_inner, oldParam, newParam));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argument = Visit(argument);
|
|
||||||
|
|
||||||
// TODO this method handler may not work for some methods (ex: method taking a Fun<> method won't have good generic arguments)
|
|
||||||
MethodInfo method = node.Method.IsGenericMethod
|
|
||||||
? node.Method.GetGenericMethodDefinition().MakeGenericMethod(instance.Type.GetGenericArguments())
|
|
||||||
: node.Method;
|
|
||||||
return node.Object != null
|
|
||||||
? Expression.Call(instance, method!, argument)
|
|
||||||
: Expression.Call(null, method!, instance, argument!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,6 +58,7 @@ namespace Kyoo.Models
|
|||||||
{
|
{
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
[SerializeIgnore] public int EpisodeID { get; set; }
|
[SerializeIgnore] public int EpisodeID { get; set; }
|
||||||
|
public int TrackIndex { get; set; }
|
||||||
public bool IsDefault
|
public bool IsDefault
|
||||||
{
|
{
|
||||||
get => isDefault;
|
get => isDefault;
|
||||||
@ -94,13 +95,17 @@ namespace Kyoo.Models
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// TODO other type of tracks should still have slugs. The slug should never be an ID. Maybe a und-number format.
|
string type = Type switch
|
||||||
if (Type != StreamType.Subtitle)
|
{
|
||||||
return null;
|
StreamType.Subtitle => "",
|
||||||
|
StreamType.Video => "video.",
|
||||||
string slug = string.IsNullOrEmpty(Language)
|
StreamType.Audio => "audio.",
|
||||||
? ID.ToString()
|
StreamType.Font => "font.",
|
||||||
: $"{Episode.Slug}.{Language}{(IsForced ? "-forced" : "")}";
|
_ => ""
|
||||||
|
};
|
||||||
|
string slug = $"{Episode.Slug}.{type}{Language}{(TrackIndex != 0 ? TrackIndex : "")}";
|
||||||
|
if (IsForced)
|
||||||
|
slug += "-forced";
|
||||||
switch (Codec)
|
switch (Codec)
|
||||||
{
|
{
|
||||||
case "ass":
|
case "ass":
|
||||||
@ -144,6 +149,7 @@ namespace Kyoo.Models
|
|||||||
return mkvLanguage switch
|
return mkvLanguage switch
|
||||||
{
|
{
|
||||||
"fre" => "fra",
|
"fre" => "fra",
|
||||||
|
null => "und",
|
||||||
_ => mkvLanguage
|
_ => mkvLanguage
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -256,6 +256,42 @@ namespace Kyoo
|
|||||||
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
|
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<T2> Map<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||||
|
[NotNull] Func<T, int, T2> mapper)
|
||||||
|
{
|
||||||
|
if (self == null)
|
||||||
|
yield break;
|
||||||
|
if (mapper == null)
|
||||||
|
throw new ArgumentNullException(nameof(mapper));
|
||||||
|
|
||||||
|
using IEnumerator<T> enumerator = self.GetEnumerator();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
yield return mapper(enumerator.Current, index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async IAsyncEnumerable<T2> MapAsync<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||||
|
[NotNull] Func<T, int, Task<T2>> mapper)
|
||||||
|
{
|
||||||
|
if (self == null)
|
||||||
|
yield break;
|
||||||
|
if (mapper == null)
|
||||||
|
throw new ArgumentNullException(nameof(mapper));
|
||||||
|
|
||||||
|
using IEnumerator<T> enumerator = self.GetEnumerator();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
yield return await mapper(enumerator.Current, index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async IAsyncEnumerable<T2> SelectAsync<T, T2>([CanBeNull] this IEnumerable<T> self,
|
public static async IAsyncEnumerable<T2> SelectAsync<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||||
[NotNull] Func<T, Task<T2>> mapper)
|
[NotNull] Func<T, Task<T2>> mapper)
|
||||||
{
|
{
|
||||||
@ -584,57 +620,5 @@ namespace Kyoo
|
|||||||
return true;
|
return true;
|
||||||
return firstID == secondID;
|
return firstID == secondID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Expression<T> Convert<T>([CanBeNull] this Expression expr)
|
|
||||||
where T : Delegate
|
|
||||||
{
|
|
||||||
Expression<T> e = expr switch
|
|
||||||
{
|
|
||||||
null => null,
|
|
||||||
LambdaExpression lambda => new ExpressionConverter<T>(lambda).VisitAndConvert(),
|
|
||||||
_ => throw new ArgumentException("Can't convert a non lambda.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return ExpressionRewrite.Rewrite<T>(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ExpressionConverter<TTo> : ExpressionVisitor
|
|
||||||
where TTo : Delegate
|
|
||||||
{
|
|
||||||
private readonly LambdaExpression _expression;
|
|
||||||
private readonly ParameterExpression[] _newParams;
|
|
||||||
|
|
||||||
internal ExpressionConverter(LambdaExpression expression)
|
|
||||||
{
|
|
||||||
_expression = expression;
|
|
||||||
|
|
||||||
Type[] paramTypes = typeof(TTo).GetGenericArguments()[..^1];
|
|
||||||
if (paramTypes.Length != _expression.Parameters.Count)
|
|
||||||
throw new ArgumentException("Parameter count from internal and external lambda are not matched.");
|
|
||||||
|
|
||||||
_newParams = new ParameterExpression[paramTypes.Length];
|
|
||||||
for (int i = 0; i < paramTypes.Length; i++)
|
|
||||||
{
|
|
||||||
if (_expression.Parameters[i].Type == paramTypes[i])
|
|
||||||
_newParams[i] = _expression.Parameters[i];
|
|
||||||
else
|
|
||||||
_newParams[i] = Expression.Parameter(paramTypes[i], _expression.Parameters[i].Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Expression<TTo> VisitAndConvert()
|
|
||||||
{
|
|
||||||
Type returnType = _expression.Type.GetGenericArguments().Last();
|
|
||||||
Expression body = _expression.ReturnType == returnType
|
|
||||||
? Visit(_expression.Body)
|
|
||||||
: Expression.Convert(Visit(_expression.Body)!, returnType);
|
|
||||||
return Expression.Lambda<TTo>(body!, _newParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Expression VisitParameter(ParameterExpression node)
|
|
||||||
{
|
|
||||||
return _newParams.FirstOrDefault(x => x.Name == node.Name) ?? node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,12 +26,7 @@ namespace Kyoo.CommonApi
|
|||||||
Expression<Func<T, bool>> defaultWhere = null)
|
Expression<Func<T, bool>> defaultWhere = null)
|
||||||
{
|
{
|
||||||
if (where == null || where.Count == 0)
|
if (where == null || where.Count == 0)
|
||||||
{
|
return defaultWhere;
|
||||||
if (defaultWhere == null)
|
|
||||||
return null;
|
|
||||||
Expression body = ExpressionRewrite.Rewrite(defaultWhere.Body);
|
|
||||||
return Expression.Lambda<Func<T, bool>>(body, defaultWhere.Parameters.First());
|
|
||||||
}
|
|
||||||
|
|
||||||
ParameterExpression param = defaultWhere?.Parameters.First() ?? Expression.Parameter(typeof(T));
|
ParameterExpression param = defaultWhere?.Parameters.First() ?? Expression.Parameter(typeof(T));
|
||||||
Expression expression = defaultWhere?.Body;
|
Expression expression = defaultWhere?.Body;
|
||||||
@ -97,8 +92,7 @@ namespace Kyoo.CommonApi
|
|||||||
expression = condition;
|
expression = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
expression = ExpressionRewrite.Rewrite(expression);
|
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)
|
||||||
|
@ -155,13 +155,7 @@ namespace Kyoo.Controllers
|
|||||||
_database.Entry(obj).State = EntityState.Added;
|
_database.Entry(obj).State = EntityState.Added;
|
||||||
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added);
|
obj.ExternalIDs.ForEach(x => _database.Entry(x).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).");
|
||||||
obj.Tracks = await obj.Tracks.SelectAsync(x =>
|
return await ValidateTracks(obj);
|
||||||
{
|
|
||||||
x.Episode = obj;
|
|
||||||
x.EpisodeID = obj.ID;
|
|
||||||
return _tracks.CreateIfNotExists(x, true);
|
|
||||||
}).ToListAsync();
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task EditRelations(Episode resource, Episode changed, bool resetOld)
|
protected override async Task EditRelations(Episode resource, Episode changed, bool resetOld)
|
||||||
@ -173,30 +167,41 @@ namespace Kyoo.Controllers
|
|||||||
{
|
{
|
||||||
ICollection<Track> oldTracks = await _tracks.GetAll(x => x.EpisodeID == resource.ID);
|
ICollection<Track> oldTracks = await _tracks.GetAll(x => x.EpisodeID == resource.ID);
|
||||||
await _tracks.DeleteRange(oldTracks);
|
await _tracks.DeleteRange(oldTracks);
|
||||||
resource.Tracks = await changed.Tracks.SelectAsync(x =>
|
resource.Tracks = changed.Tracks;
|
||||||
{
|
await ValidateTracks(resource);
|
||||||
x.Episode = resource;
|
|
||||||
x.EpisodeID = resource.ID;
|
|
||||||
return _tracks.Create(x);
|
|
||||||
}).ToListAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed.ExternalIDs != null || resetOld)
|
if (changed.ExternalIDs != null || resetOld)
|
||||||
{
|
{
|
||||||
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync();
|
||||||
resource.ExternalIDs = changed.ExternalIDs?.Select(x =>
|
resource.ExternalIDs = changed.ExternalIDs;
|
||||||
{
|
|
||||||
x.Provider = null;
|
|
||||||
return x;
|
|
||||||
}).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Validate(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Episode> ValidateTracks(Episode resource)
|
||||||
|
{
|
||||||
|
resource.Tracks = await resource.Tracks.MapAsync((x, i) =>
|
||||||
|
{
|
||||||
|
x.Episode = resource;
|
||||||
|
x.TrackIndex = resource.Tracks.Take(i).Count(y => x.Language == y.Language
|
||||||
|
&& x.IsForced == y.IsForced
|
||||||
|
&& x.Codec == y.Codec
|
||||||
|
&& x.Type == y.Type);
|
||||||
|
return _tracks.CreateIfNotExists(x, true);
|
||||||
|
}).ToListAsync();
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task Validate(Episode resource)
|
protected override async Task Validate(Episode resource)
|
||||||
{
|
{
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
await resource.ExternalIDs.ForEachAsync(async id =>
|
resource.ExternalIDs = resource.ExternalIDs?.Select(x =>
|
||||||
id.Provider = await _providers.CreateIfNotExists(id.Provider, true));
|
{
|
||||||
|
x.Provider = null;
|
||||||
|
return x;
|
||||||
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Delete(string showSlug, int seasonNumber, int episodeNumber)
|
public async Task Delete(string showSlug, int seasonNumber, int episodeNumber)
|
||||||
|
@ -99,7 +99,8 @@ namespace Kyoo.Controllers
|
|||||||
protected override async Task Validate(Show resource)
|
protected override async Task Validate(Show resource)
|
||||||
{
|
{
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio, true);
|
if (resource.Studio != null)
|
||||||
|
resource.Studio = await _studios.CreateIfNotExists(resource.Studio, true);
|
||||||
resource.GenreLinks = await resource.Genres
|
resource.GenreLinks = await resource.Genres
|
||||||
.SelectAsync(async x => Link.UCreate(resource, await _genres.CreateIfNotExists(x, true)))
|
.SelectAsync(async x => Link.UCreate(resource, await _genres.CreateIfNotExists(x, true)))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="Symlink views to output - Linux" AfterTargets="Build" Condition="$(Configuration) == 'Debug' And $(OS) == 'Unix'">
|
<Target Name="Symlink views to output - Linux" AfterTargets="Build" Condition="$(Configuration) == 'Debug' And $(OS) == 'Unix'">
|
||||||
<Exec WorkingDirectory="$(OutputPath)" Command="ln -fs ../../../Views"/>
|
<Exec WorkingDirectory="$(OutputPath)" Command="ln -fs ../../../Views" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="Compile the transcoder" BeforeTargets="BeforeBuild">
|
<Target Name="Compile the transcoder" BeforeTargets="BeforeBuild">
|
||||||
|
@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Kyoo.Models.DatabaseMigrations.Internal
|
namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||||
{
|
{
|
||||||
[DbContext(typeof(DatabaseContext))]
|
[DbContext(typeof(DatabaseContext))]
|
||||||
[Migration("20210316095337_Initial")]
|
[Migration("20210317180448_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -491,6 +491,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
|||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("TrackIndex")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.Property<int>("Type")
|
b.Property<int>("Type")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
@ -397,6 +397,7 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
|||||||
ID = table.Column<int>(type: "integer", nullable: false)
|
ID = table.Column<int>(type: "integer", nullable: false)
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
EpisodeID = table.Column<int>(type: "integer", nullable: false),
|
EpisodeID = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
TrackIndex = table.Column<int>(type: "integer", nullable: false),
|
||||||
IsDefault = table.Column<bool>(type: "boolean", nullable: false),
|
IsDefault = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
IsForced = table.Column<bool>(type: "boolean", nullable: false),
|
IsForced = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
IsExternal = table.Column<bool>(type: "boolean", nullable: false),
|
IsExternal = table.Column<bool>(type: "boolean", nullable: false),
|
@ -489,6 +489,9 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
|||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("TrackIndex")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.Property<int>("Type")
|
b.Property<int>("Type")
|
||||||
.HasColumnType("integer");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
56
Kyoo/Views/API/TrackApi.cs
Normal file
56
Kyoo/Views/API/TrackApi.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.CommonApi;
|
||||||
|
using Kyoo.Controllers;
|
||||||
|
using Kyoo.Models;
|
||||||
|
using Kyoo.Models.Exceptions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Kyoo.Api
|
||||||
|
{
|
||||||
|
[Route("api/track")]
|
||||||
|
[Route("api/tracks")]
|
||||||
|
[ApiController]
|
||||||
|
public class TrackApi : CrudApi<Track>
|
||||||
|
{
|
||||||
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
|
public TrackApi(ILibraryManager libraryManager, IConfiguration configuration)
|
||||||
|
: base(libraryManager.TrackRepository, configuration)
|
||||||
|
{
|
||||||
|
_libraryManager = libraryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}/episode")]
|
||||||
|
[Authorize(Policy = "Read")]
|
||||||
|
public async Task<ActionResult<Episode>> GetEpisode(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _libraryManager.GetEpisode(x => x.Tracks.Any(y => y.ID == id));
|
||||||
|
}
|
||||||
|
catch (ItemNotFound)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{slug}/episode")]
|
||||||
|
[Authorize(Policy = "Read")]
|
||||||
|
public async Task<ActionResult<Episode>> GetEpisode(string slug)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO This won't work with the local repository implementation.
|
||||||
|
// TODO Implement something like this (a dotnet-ef's QueryCompilationContext): https://stackoverflow.com/questions/62687811/how-can-i-convert-a-custom-function-to-a-sql-expression-for-entity-framework-cor
|
||||||
|
return await _libraryManager.GetEpisode(x => x.Tracks.Any(y => y.Slug == slug));
|
||||||
|
}
|
||||||
|
catch (ItemNotFound)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user