mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Use null safety
This commit is contained in:
parent
e13f9c6aa8
commit
8b102b083f
@ -249,7 +249,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(int showID, int seasonNumber);
|
||||
Task<Season?> GetOrDefault(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
|
||||
@ -257,7 +257,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
|
||||
Task<Season?> GetOrDefault(string showSlug, int seasonNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -294,7 +294,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
|
||||
Task<Episode?> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found.
|
||||
@ -303,7 +303,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
|
||||
Task<Episode?> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID and it's absolute number.
|
||||
|
@ -46,7 +46,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="desendant">
|
||||
/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order.
|
||||
/// </param>
|
||||
public By(Expression<Func<T, object>> key, bool desendant = false)
|
||||
public By(Expression<Func<T, object?>> key, bool desendant = false)
|
||||
: this(Utility.GetPropertyName(key), desendant) { }
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,8 @@ namespace Kyoo.Core.Controllers
|
||||
public class IdentifierRouteConstraint : IRouteConstraint
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool Match(HttpContext httpContext,
|
||||
IRouter route,
|
||||
public bool Match(HttpContext? httpContext,
|
||||
IRouter? route,
|
||||
string routeKey,
|
||||
RouteValueDictionary values,
|
||||
RouteDirection routeDirection)
|
||||
|
@ -74,15 +74,15 @@ namespace Kyoo.Core.Controllers
|
||||
public LibraryManager(IEnumerable<IBaseRepository> repositories)
|
||||
{
|
||||
_repositories = repositories.ToArray();
|
||||
LibraryItemRepository = GetRepository<LibraryItem>() as ILibraryItemRepository;
|
||||
CollectionRepository = GetRepository<Collection>() as ICollectionRepository;
|
||||
MovieRepository = GetRepository<Movie>() as IMovieRepository;
|
||||
ShowRepository = GetRepository<Show>() as IShowRepository;
|
||||
SeasonRepository = GetRepository<Season>() as ISeasonRepository;
|
||||
EpisodeRepository = GetRepository<Episode>() as IEpisodeRepository;
|
||||
PeopleRepository = GetRepository<People>() as IPeopleRepository;
|
||||
StudioRepository = GetRepository<Studio>() as IStudioRepository;
|
||||
UserRepository = GetRepository<User>() as IUserRepository;
|
||||
LibraryItemRepository = (ILibraryItemRepository)GetRepository<LibraryItem>();
|
||||
CollectionRepository = (ICollectionRepository)GetRepository<Collection>();
|
||||
MovieRepository = (IMovieRepository)GetRepository<Movie>();
|
||||
ShowRepository = (IShowRepository)GetRepository<Show>();
|
||||
SeasonRepository = (ISeasonRepository)GetRepository<Season>();
|
||||
EpisodeRepository = (IEpisodeRepository)GetRepository<Episode>();
|
||||
PeopleRepository = (IPeopleRepository)GetRepository<People>();
|
||||
StudioRepository = (IStudioRepository)GetRepository<Studio>();
|
||||
UserRepository = (IUserRepository)GetRepository<User>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -140,46 +140,46 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<T> GetOrDefault<T>(int id)
|
||||
public async Task<T?> GetOrDefault<T>(int id)
|
||||
where T : class, IResource
|
||||
{
|
||||
return await GetRepository<T>().GetOrDefault(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<T> GetOrDefault<T>(string slug)
|
||||
public async Task<T?> GetOrDefault<T>(string slug)
|
||||
where T : class, IResource
|
||||
{
|
||||
return await GetRepository<T>().GetOrDefault(slug);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<T> GetOrDefault<T>(Expression<Func<T, bool>> where, Sort<T> sortBy)
|
||||
public async Task<T?> GetOrDefault<T>(Expression<Func<T, bool>> where, Sort<T>? sortBy)
|
||||
where T : class, IResource
|
||||
{
|
||||
return await GetRepository<T>().GetOrDefault(where, sortBy);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Season> GetOrDefault(int showID, int seasonNumber)
|
||||
public async Task<Season?> GetOrDefault(int showID, int seasonNumber)
|
||||
{
|
||||
return await SeasonRepository.GetOrDefault(showID, seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Season> GetOrDefault(string showSlug, int seasonNumber)
|
||||
public async Task<Season?> GetOrDefault(string showSlug, int seasonNumber)
|
||||
{
|
||||
return await SeasonRepository.GetOrDefault(showSlug, seasonNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||
public async Task<Episode?> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return await EpisodeRepository.GetOrDefault(showID, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
|
||||
public async Task<Episode?> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return await EpisodeRepository.GetOrDefault(showSlug, seasonNumber, episodeNumber);
|
||||
}
|
||||
@ -238,7 +238,7 @@ namespace Kyoo.Core.Controllers
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
|
||||
object existingValue = obj.GetType()
|
||||
object? existingValue = obj.GetType()
|
||||
.GetProperties()
|
||||
.FirstOrDefault(x => string.Equals(x.Name, memberName, StringComparison.InvariantCultureIgnoreCase))
|
||||
?.GetValue(obj);
|
||||
@ -248,11 +248,11 @@ namespace Kyoo.Core.Controllers
|
||||
return (obj, member: memberName) switch
|
||||
{
|
||||
(Collection c, nameof(Collection.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Collections.Any(y => y.Id == obj.Id))
|
||||
.GetAll(x => x.Collections!.Any(y => y.Id == obj.Id))
|
||||
.Then(x => c.Shows = x),
|
||||
|
||||
(Collection c, nameof(Collection.Movies)) => MovieRepository
|
||||
.GetAll(x => x.Collections.Any(y => y.Id == obj.Id))
|
||||
.GetAll(x => x.Collections!.Any(y => y.Id == obj.Id))
|
||||
.Then(x => c.Movies = x),
|
||||
|
||||
|
||||
@ -261,11 +261,11 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x => m.People = x),
|
||||
|
||||
(Movie m, nameof(Movie.Collections)) => CollectionRepository
|
||||
.GetAll(x => x.Movies.Any(y => y.Id == obj.Id))
|
||||
.GetAll(x => x.Movies!.Any(y => y.Id == obj.Id))
|
||||
.Then(x => m.Collections = x),
|
||||
|
||||
(Movie m, nameof(Movie.Studio)) => StudioRepository
|
||||
.GetOrDefault(x => x.Movies.Any(y => y.Id == obj.Id))
|
||||
.GetOrDefault(x => x.Movies!.Any(y => y.Id == obj.Id))
|
||||
.Then(x =>
|
||||
{
|
||||
m.Studio = x;
|
||||
@ -278,21 +278,21 @@ namespace Kyoo.Core.Controllers
|
||||
.Then(x => s.People = x),
|
||||
|
||||
(Show s, nameof(Show.Seasons)) => _SetRelation(s,
|
||||
SeasonRepository.GetAll(x => x.Show.Id == obj.Id),
|
||||
SeasonRepository.GetAll(x => x.Show!.Id == obj.Id),
|
||||
(x, y) => x.Seasons = y,
|
||||
(x, y) => { x.Show = y; x.ShowId = y.Id; }),
|
||||
|
||||
(Show s, nameof(Show.Episodes)) => _SetRelation(s,
|
||||
EpisodeRepository.GetAll(x => x.Show.Id == obj.Id),
|
||||
EpisodeRepository.GetAll(x => x.Show!.Id == obj.Id),
|
||||
(x, y) => x.Episodes = y,
|
||||
(x, y) => { x.Show = y; x.ShowId = y.Id; }),
|
||||
|
||||
(Show s, nameof(Show.Collections)) => CollectionRepository
|
||||
.GetAll(x => x.Shows.Any(y => y.Id == obj.Id))
|
||||
.GetAll(x => x.Shows!.Any(y => y.Id == obj.Id))
|
||||
.Then(x => s.Collections = x),
|
||||
|
||||
(Show s, nameof(Show.Studio)) => StudioRepository
|
||||
.GetOrDefault(x => x.Shows.Any(y => y.Id == obj.Id))
|
||||
.GetOrDefault(x => x.Shows!.Any(y => y.Id == obj.Id))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Studio = x;
|
||||
@ -301,12 +301,12 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
|
||||
(Season s, nameof(Season.Episodes)) => _SetRelation(s,
|
||||
EpisodeRepository.GetAll(x => x.Season.Id == obj.Id),
|
||||
EpisodeRepository.GetAll(x => x.Season!.Id == obj.Id),
|
||||
(x, y) => x.Episodes = y,
|
||||
(x, y) => { x.Season = y; x.SeasonId = y.Id; }),
|
||||
|
||||
(Season s, nameof(Season.Show)) => ShowRepository
|
||||
.GetOrDefault(x => x.Seasons.Any(y => y.Id == obj.Id))
|
||||
.GetOrDefault(x => x.Seasons!.Any(y => y.Id == obj.Id))
|
||||
.Then(x =>
|
||||
{
|
||||
s.Show = x;
|
||||
@ -315,7 +315,7 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
|
||||
(Episode e, nameof(Episode.Show)) => ShowRepository
|
||||
.GetOrDefault(x => x.Episodes.Any(y => y.Id == obj.Id))
|
||||
.GetOrDefault(x => x.Episodes!.Any(y => y.Id == obj.Id))
|
||||
.Then(x =>
|
||||
{
|
||||
e.Show = x;
|
||||
@ -323,7 +323,7 @@ namespace Kyoo.Core.Controllers
|
||||
}),
|
||||
|
||||
(Episode e, nameof(Episode.Season)) => SeasonRepository
|
||||
.GetOrDefault(x => x.Episodes.Any(y => y.Id == e.Id))
|
||||
.GetOrDefault(x => x.Episodes!.Any(y => y.Id == e.Id))
|
||||
.Then(x =>
|
||||
{
|
||||
e.Season = x;
|
||||
@ -344,11 +344,11 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
|
||||
(Studio s, nameof(Studio.Shows)) => ShowRepository
|
||||
.GetAll(x => x.Studio.Id == obj.Id)
|
||||
.GetAll(x => x.Studio!.Id == obj.Id)
|
||||
.Then(x => s.Shows = x),
|
||||
|
||||
(Studio s, nameof(Studio.Movies)) => MovieRepository
|
||||
.GetAll(x => x.Studio.Id == obj.Id)
|
||||
.GetAll(x => x.Studio!.Id == obj.Id)
|
||||
.Then(x => s.Movies = x),
|
||||
|
||||
|
||||
@ -362,51 +362,51 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromShow(showID, where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromShow(showSlug, where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromPeople(id, where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return PeopleRepository.GetFromPeople(slug, where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
public Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>>? where = null,
|
||||
Sort<T>? sort = default,
|
||||
Pagination? limit = default)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetRepository<T>().GetAll(where, sort, limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> GetCount<T>(Expression<Func<T, bool>> where = null)
|
||||
public Task<int> GetCount<T>(Expression<Func<T, bool>>? where = null)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetRepository<T>().GetCount(where);
|
||||
|
@ -77,7 +77,7 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||
public Task<Episode?> GetOrDefault(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
@ -85,9 +85,9 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
|
||||
public Task<Episode?> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show!.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber).Then(SetBackingImage);
|
||||
}
|
||||
@ -95,7 +95,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode ret = await GetOrDefault(showID, seasonNumber, episodeNumber);
|
||||
Episode? ret = await GetOrDefault(showID, seasonNumber, episodeNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No episode S{seasonNumber}E{episodeNumber} found on the show {showID}.");
|
||||
return ret;
|
||||
@ -104,24 +104,30 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc />
|
||||
public async Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
Episode ret = await GetOrDefault(showSlug, seasonNumber, episodeNumber);
|
||||
Episode? ret = await GetOrDefault(showSlug, seasonNumber, episodeNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No episode S{seasonNumber}E{episodeNumber} found on the show {showSlug}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
public async Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||
Episode? ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||
&& x.AbsoluteNumber == absoluteNumber).Then(SetBackingImage);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
public async Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
Episode? ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show!.Slug == showSlug
|
||||
&& x.AbsoluteNumber == absoluteNumber).Then(SetBackingImage);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -131,13 +137,13 @@ namespace Kyoo.Core.Controllers
|
||||
_database.Episodes
|
||||
.Include(x => x.Show)
|
||||
.Where(x => x.EpisodeNumber != null || x.AbsoluteNumber != null)
|
||||
.Where(_database.Like<Episode>(x => x.Name, $"%{query}%"))
|
||||
.Where(_database.Like<Episode>(x => x.Name!, $"%{query}%"))
|
||||
)
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
foreach (Episode ep in ret)
|
||||
{
|
||||
ep.Show.Episodes = null;
|
||||
ep.Show!.Episodes = null;
|
||||
SetBackingImage(ep);
|
||||
}
|
||||
return ret;
|
||||
@ -152,7 +158,7 @@ namespace Kyoo.Core.Controllers
|
||||
await _database.SaveChangesAsync(() =>
|
||||
obj.SeasonNumber != null && obj.EpisodeNumber != null
|
||||
? Get(obj.ShowId, obj.SeasonNumber.Value, obj.EpisodeNumber.Value)
|
||||
: GetAbsolute(obj.ShowId, obj.AbsoluteNumber.Value));
|
||||
: GetAbsolute(obj.ShowId, obj.AbsoluteNumber!.Value));
|
||||
OnResourceCreated(obj);
|
||||
return obj;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <param name="query">The query to sort.</param>
|
||||
/// <param name="sortBy">How to sort the query</param>
|
||||
/// <returns>The newly sorted query.</returns>
|
||||
protected IOrderedQueryable<T> Sort(IQueryable<T> query, Sort<T> sortBy = null)
|
||||
protected IOrderedQueryable<T> Sort(IQueryable<T> query, Sort<T>? sortBy = null)
|
||||
{
|
||||
sortBy ??= DefaultSort;
|
||||
|
||||
@ -138,7 +138,7 @@ namespace Kyoo.Core.Controllers
|
||||
: (greaterThan ? Expression.GreaterThan : Expression.LessThan);
|
||||
}
|
||||
|
||||
private record SortIndicator(string key, bool desc, string? seed);
|
||||
private record SortIndicator(string Key, bool Desc, string? Seed);
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter (where) expression on the query to skip everything before/after the referenceID.
|
||||
@ -158,7 +158,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <param name="next">True if the following page should be returned, false for the previous.</param>
|
||||
/// <returns>An expression ready to be added to a Where close of a sorted query to handle the AfterID</returns>
|
||||
protected Expression<Func<T, bool>> KeysetPaginate(
|
||||
Sort<T> sort,
|
||||
Sort<T>? sort,
|
||||
T reference,
|
||||
bool next = true)
|
||||
{
|
||||
@ -170,7 +170,7 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
void GetRandomSortKeys(string seed, out Expression left, out Expression right)
|
||||
{
|
||||
MethodInfo concat = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) });
|
||||
MethodInfo concat = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })!;
|
||||
Expression id = Expression.Call(Expression.Property(x, "ID"), nameof(int.ToString), null);
|
||||
Expression xrng = Expression.Call(concat, Expression.Constant(seed), id);
|
||||
right = Expression.Call(typeof(DatabaseContext), nameof(DatabaseContext.MD5), null, Expression.Constant($"{seed}{reference.Id}"));
|
||||
@ -193,14 +193,14 @@ namespace Kyoo.Core.Controllers
|
||||
IEnumerable<SortIndicator> sorts = GetSortsBy(sort)
|
||||
.Append(new SortIndicator("Id", false, null));
|
||||
|
||||
BinaryExpression filter = null;
|
||||
BinaryExpression? filter = null;
|
||||
List<SortIndicator> previousSteps = new();
|
||||
// TODO: Add an outer query >= for perf
|
||||
// PERF: See https://use-the-index-luke.com/sql/partial-results/fetch-next-page#sb-equivalent-logic
|
||||
foreach ((string key, bool desc, string? seed) in sorts)
|
||||
{
|
||||
BinaryExpression compare = null;
|
||||
PropertyInfo property = key != "random"
|
||||
BinaryExpression? compare = null;
|
||||
PropertyInfo? property = key != "random"
|
||||
? typeof(T).GetProperty(key)
|
||||
: null;
|
||||
|
||||
@ -268,7 +268,7 @@ namespace Kyoo.Core.Controllers
|
||||
return Expression.Lambda<Func<T, bool>>(filter!, x);
|
||||
}
|
||||
|
||||
protected void SetBackingImage(T obj)
|
||||
protected void SetBackingImage(T? obj)
|
||||
{
|
||||
if (obj is not IThumbnails thumbs)
|
||||
return;
|
||||
@ -298,7 +298,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <returns>The tracked resource with the given ID</returns>
|
||||
protected virtual async Task<T> GetWithTracking(int id)
|
||||
{
|
||||
T ret = await Database.Set<T>().AsTracking().FirstOrDefaultAsync(x => x.Id == id);
|
||||
T? ret = await Database.Set<T>().AsTracking().FirstOrDefaultAsync(x => x.Id == id);
|
||||
SetBackingImage(ret);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}");
|
||||
@ -308,7 +308,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(int id)
|
||||
{
|
||||
T ret = await GetOrDefault(id);
|
||||
T? ret = await GetOrDefault(id);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No {typeof(T).Name} found with the id {id}");
|
||||
return ret;
|
||||
@ -317,7 +317,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(string slug)
|
||||
{
|
||||
T ret = await GetOrDefault(slug);
|
||||
T? ret = await GetOrDefault(slug);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No {typeof(T).Name} found with the slug {slug}");
|
||||
return ret;
|
||||
@ -326,20 +326,20 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<T> Get(Expression<Func<T, bool>> where)
|
||||
{
|
||||
T ret = await GetOrDefault(where);
|
||||
T? ret = await GetOrDefault(where);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No {typeof(T).Name} found with the given predicate.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T> GetOrDefault(int id)
|
||||
public virtual Task<T?> GetOrDefault(int id)
|
||||
{
|
||||
return Database.Set<T>().FirstOrDefaultAsync(x => x.Id == id).Then(SetBackingImage);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T> GetOrDefault(string slug)
|
||||
public virtual Task<T?> GetOrDefault(string slug)
|
||||
{
|
||||
if (slug == "random")
|
||||
return Database.Set<T>().OrderBy(x => EF.Functions.Random()).FirstOrDefaultAsync().Then(SetBackingImage);
|
||||
@ -347,7 +347,7 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task<T> GetOrDefault(Expression<Func<T, bool>> where, Sort<T> sortBy = default)
|
||||
public virtual Task<T?> GetOrDefault(Expression<Func<T, bool>> where, Sort<T>? sortBy = default)
|
||||
{
|
||||
return Sort(Database.Set<T>(), sortBy).FirstOrDefaultAsync(where).Then(SetBackingImage);
|
||||
}
|
||||
@ -356,9 +356,9 @@ namespace Kyoo.Core.Controllers
|
||||
public abstract Task<ICollection<T>> Search(string query);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
public virtual async Task<ICollection<T>> GetAll(Expression<Func<T, bool>>? where = null,
|
||||
Sort<T>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return (await ApplyFilters(Database.Set<T>(), where, sort, limit))
|
||||
.Select(SetBackingImageSelf).ToList();
|
||||
@ -373,9 +373,9 @@ namespace Kyoo.Core.Controllers
|
||||
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
||||
/// <returns>The filtered query</returns>
|
||||
protected async Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
|
||||
Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<T, bool>>? where = null,
|
||||
Sort<T>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
query = Sort(query, sort);
|
||||
if (where != null)
|
||||
@ -395,7 +395,7 @@ namespace Kyoo.Core.Controllers
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual Task<int> GetCount(Expression<Func<T, bool>> where = null)
|
||||
public virtual Task<int> GetCount(Expression<Func<T, bool>>? where = null)
|
||||
{
|
||||
IQueryable<T> query = Database.Set<T>();
|
||||
if (where != null)
|
||||
@ -411,11 +411,11 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
await _thumbs.DownloadImages(thumbs);
|
||||
if (thumbs.Poster != null)
|
||||
Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry.State = EntityState.Added;
|
||||
Database.Entry(thumbs).Reference(x => x.Poster).TargetEntry!.State = EntityState.Added;
|
||||
if (thumbs.Thumbnail != null)
|
||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry.State = EntityState.Added;
|
||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).TargetEntry!.State = EntityState.Added;
|
||||
if (thumbs.Logo != null)
|
||||
Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry.State = EntityState.Added;
|
||||
Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry!.State = EntityState.Added;
|
||||
}
|
||||
SetBackingImage(obj);
|
||||
return obj;
|
||||
@ -444,7 +444,7 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
T old = await GetOrDefault(obj.Slug);
|
||||
T? old = await GetOrDefault(obj.Slug);
|
||||
if (old != null)
|
||||
return old;
|
||||
|
||||
@ -544,7 +544,7 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
MethodInfo setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod();
|
||||
MethodInfo? setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod();
|
||||
if (setter != null)
|
||||
setter.Invoke(resource, new object[] { resource.Slug + '!' });
|
||||
else
|
||||
|
@ -126,7 +126,7 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
if (changed.People != null)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.People).LoadAsync();
|
||||
await Database.Entry(resource).Collection(x => x.People!).LoadAsync();
|
||||
resource.People = changed.People;
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
foreach (PeopleRole role in resource.Roles)
|
||||
{
|
||||
role.Show = _database.LocalEntity<Show>(role.Show.Slug)
|
||||
role.Show = _database.LocalEntity<Show>(role.Show!.Slug)
|
||||
?? await _shows.Value.CreateIfNotExists(role.Show);
|
||||
role.ShowID = role.Show.Id;
|
||||
_database.Entry(role).State = EntityState.Added;
|
||||
@ -109,7 +109,7 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
if (changed.Roles != null)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.Roles).LoadAsync();
|
||||
await Database.Entry(resource).Collection(x => x.Roles!).LoadAsync();
|
||||
resource.Roles = changed.Roles;
|
||||
}
|
||||
}
|
||||
@ -125,9 +125,9 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return Task.FromResult<ICollection<PeopleRole>>(new List<PeopleRole>());
|
||||
// ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
|
||||
@ -146,9 +146,9 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return Task.FromResult<ICollection<PeopleRole>>(new List<PeopleRole>());
|
||||
// ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles
|
||||
@ -169,9 +169,9 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return Task.FromResult<ICollection<PeopleRole>>(new List<PeopleRole>());
|
||||
// ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
|
||||
@ -189,9 +189,9 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default)
|
||||
Expression<Func<PeopleRole, bool>>? where = null,
|
||||
Sort<PeopleRole>? sort = default,
|
||||
Pagination? limit = default)
|
||||
{
|
||||
return Task.FromResult<ICollection<PeopleRole>>(new List<PeopleRole>());
|
||||
// ICollection<PeopleRole> roles = await ApplyFilters(_database.PeopleRoles
|
||||
|
@ -71,7 +71,7 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc/>
|
||||
public async Task<Season> Get(int showID, int seasonNumber)
|
||||
{
|
||||
Season ret = await GetOrDefault(showID, seasonNumber);
|
||||
Season? ret = await GetOrDefault(showID, seasonNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showID}");
|
||||
return ret;
|
||||
@ -80,23 +80,23 @@ namespace Kyoo.Core.Controllers
|
||||
/// <inheritdoc/>
|
||||
public async Task<Season> Get(string showSlug, int seasonNumber)
|
||||
{
|
||||
Season ret = await GetOrDefault(showSlug, seasonNumber);
|
||||
Season? ret = await GetOrDefault(showSlug, seasonNumber);
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException($"No season {seasonNumber} found for the show {showSlug}");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<Season> GetOrDefault(int showID, int seasonNumber)
|
||||
public Task<Season?> GetOrDefault(int showID, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowId == showID
|
||||
&& x.SeasonNumber == seasonNumber).Then(SetBackingImage);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<Season> GetOrDefault(string showSlug, int seasonNumber)
|
||||
public Task<Season?> GetOrDefault(string showSlug, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.Show!.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber).Then(SetBackingImage);
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ namespace Kyoo.Core.Controllers
|
||||
{
|
||||
return (await Sort(
|
||||
_database.Seasons
|
||||
.Where(_database.Like<Season>(x => x.Name, $"%{query}%"))
|
||||
.Where(_database.Like<Season>(x => x.Name!, $"%{query}%"))
|
||||
)
|
||||
.Take(20)
|
||||
.ToListAsync())
|
||||
|
@ -21,6 +21,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
using Kyoo.Postgresql;
|
||||
using Kyoo.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -129,17 +130,20 @@ namespace Kyoo.Core.Controllers
|
||||
|
||||
if (changed.People != null)
|
||||
{
|
||||
await Database.Entry(resource).Collection(x => x.People).LoadAsync();
|
||||
await Database.Entry(resource).Collection(x => x.People!).LoadAsync();
|
||||
resource.People = changed.People;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<string> GetSlug(int showID)
|
||||
public async Task<string> GetSlug(int showID)
|
||||
{
|
||||
return _database.Shows.Where(x => x.Id == showID)
|
||||
string? ret = await _database.Shows.Where(x => x.Id == showID)
|
||||
.Select(x => x.Slug)
|
||||
.FirstOrDefaultAsync();
|
||||
if (ret == null)
|
||||
throw new ItemNotFoundException();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -69,7 +69,7 @@ namespace Kyoo.Core.Controllers
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
if (obj.Logo != null)
|
||||
_database.Entry(obj).Reference(x => x.Logo).TargetEntry.State = EntityState.Added;
|
||||
_database.Entry(obj).Reference(x => x.Logo).TargetEntry!.State = EntityState.Added;
|
||||
await _database.SaveChangesAsync(() => Get(obj.Slug));
|
||||
OnResourceCreated(obj);
|
||||
return obj;
|
||||
|
@ -84,7 +84,7 @@ namespace Kyoo.Core
|
||||
options.InvalidModelStateResponseFactory = ctx =>
|
||||
{
|
||||
string[] errors = ctx.ModelState
|
||||
.SelectMany(x => x.Value.Errors)
|
||||
.SelectMany(x => x.Value!.Errors)
|
||||
.Select(x => x.ErrorMessage)
|
||||
.ToArray();
|
||||
return new BadRequestObjectResult(new RequestError(errors));
|
||||
|
@ -1,47 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A class containing helper methods.
|
||||
/// </summary>
|
||||
public static class Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// An helper method to get json content from an http server. This is a temporary thing and will probably be
|
||||
/// replaced by a call to the function of the same name in the <c>System.Net.Http.Json</c> namespace when .net6
|
||||
/// gets released.
|
||||
/// </summary>
|
||||
/// <param name="client">The http server to use.</param>
|
||||
/// <param name="url">The url to retrieve</param>
|
||||
/// <typeparam name="T">The type of object to convert</typeparam>
|
||||
/// <returns>A T representing the json contained at the given url.</returns>
|
||||
public static async Task<T> GetFromJsonAsync<T>(this HttpClient client, string url)
|
||||
{
|
||||
HttpResponseMessage ret = await client.GetAsync(url);
|
||||
ret.EnsureSuccessStatusCode();
|
||||
string content = await ret.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<T>(content);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Kyoo.Core</AssemblyName>
|
||||
<RootNamespace>Kyoo.Core</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -48,9 +48,9 @@ namespace Kyoo.Core.Api
|
||||
/// <param name="right">The second parameter to compare.</param>
|
||||
/// <returns>A comparison expression compatible with strings</returns>
|
||||
public static BinaryExpression StringCompatibleExpression(
|
||||
[NotNull] Func<Expression, Expression, BinaryExpression> operand,
|
||||
[NotNull] Expression left,
|
||||
[NotNull] Expression right)
|
||||
Func<Expression, Expression, BinaryExpression> operand,
|
||||
Expression left,
|
||||
Expression right)
|
||||
{
|
||||
if (left.Type != typeof(string))
|
||||
return operand(left, right);
|
||||
@ -69,14 +69,14 @@ namespace Kyoo.Core.Api
|
||||
/// <typeparam name="T">The type to create filters for.</typeparam>
|
||||
/// <exception cref="ArgumentException">A filter is invalid.</exception>
|
||||
/// <returns>An expression representing the filters that can be used anywhere or compiled</returns>
|
||||
public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where,
|
||||
Expression<Func<T, bool>> defaultWhere = null)
|
||||
public static Expression<Func<T, bool>>? ParseWhere<T>(Dictionary<string, string>? where,
|
||||
Expression<Func<T, bool>>? defaultWhere = null)
|
||||
{
|
||||
if (where == null || where.Count == 0)
|
||||
return defaultWhere;
|
||||
|
||||
ParameterExpression param = defaultWhere?.Parameters.First() ?? Expression.Parameter(typeof(T));
|
||||
Expression expression = defaultWhere?.Body;
|
||||
Expression? expression = defaultWhere?.Body;
|
||||
|
||||
foreach ((string key, string desired) in where)
|
||||
{
|
||||
@ -91,17 +91,17 @@ namespace Kyoo.Core.Api
|
||||
value = desired.Substring(desired.IndexOf(':') + 1);
|
||||
}
|
||||
|
||||
PropertyInfo property = typeof(T).GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
|
||||
PropertyInfo? property = typeof(T).GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
|
||||
if (property == null)
|
||||
throw new ArgumentException($"No filterable parameter with the name {key}.");
|
||||
MemberExpression propertyExpr = Expression.Property(param, property);
|
||||
|
||||
ConstantExpression valueExpr = null;
|
||||
ConstantExpression? valueExpr = null;
|
||||
bool isList = typeof(IEnumerable).IsAssignableFrom(propertyExpr.Type) && propertyExpr.Type != typeof(string);
|
||||
if (operand != "ctn" && !typeof(IResource).IsAssignableFrom(propertyExpr.Type) && !isList)
|
||||
{
|
||||
Type propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
|
||||
object val;
|
||||
object? val;
|
||||
try
|
||||
{
|
||||
val = string.IsNullOrEmpty(value) || value.Equals("null", StringComparison.OrdinalIgnoreCase)
|
||||
@ -110,7 +110,7 @@ namespace Kyoo.Core.Api
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
throw new ArgumentException("Comparing two differents value's type.");
|
||||
throw new ArgumentException("Comparing two different value's type.");
|
||||
}
|
||||
|
||||
valueExpr = Expression.Constant(val, property.PropertyType);
|
||||
@ -166,7 +166,7 @@ namespace Kyoo.Core.Api
|
||||
if (xProperty.Type == typeof(string))
|
||||
{
|
||||
// x.PROPRETY.Contains(value);
|
||||
return Expression.Call(xProperty, typeof(string).GetMethod("Contains", new[] { typeof(string) }), Expression.Constant(value));
|
||||
return Expression.Call(xProperty, typeof(string).GetMethod("Contains", new[] { typeof(string) })!, Expression.Constant(value));
|
||||
}
|
||||
|
||||
// x.PROPERTY is either a List<> or a []
|
||||
@ -176,7 +176,8 @@ namespace Kyoo.Core.Api
|
||||
{
|
||||
return Expression.Call(typeof(Enumerable), "Contains", new[] { inner }, xProperty, Expression.Constant(value));
|
||||
}
|
||||
else if (inner.IsEnum && Enum.TryParse(inner, value, true, out object enumValue))
|
||||
|
||||
if (inner.IsEnum && Enum.TryParse(inner, value, true, out object? enumValue))
|
||||
{
|
||||
return Expression.Call(typeof(Enumerable), "Contains", new[] { inner }, xProperty, Expression.Constant(enumValue));
|
||||
}
|
||||
@ -185,7 +186,7 @@ namespace Kyoo.Core.Api
|
||||
throw new ArgumentException("Contain (ctn) not appliable for this property.");
|
||||
|
||||
// x => x.PROPERTY.Any(y => y.Slug == value)
|
||||
Expression ret = null;
|
||||
Expression? ret = null;
|
||||
ParameterExpression y = Expression.Parameter(inner, "y");
|
||||
foreach (string val in value.Split(','))
|
||||
{
|
||||
@ -197,7 +198,7 @@ namespace Kyoo.Core.Api
|
||||
else
|
||||
ret = Expression.AndAlso(ret, iteration);
|
||||
}
|
||||
return ret;
|
||||
return ret!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models;
|
||||
@ -69,7 +70,7 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<T>> Get(Identifier identifier)
|
||||
{
|
||||
T ret = await identifier.Match(
|
||||
T? ret = await identifier.Match(
|
||||
id => Repository.GetOrDefault(id),
|
||||
slug => Repository.GetOrDefault(slug)
|
||||
);
|
||||
@ -235,7 +236,11 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||
public async Task<IActionResult> Delete([FromQuery] Dictionary<string, string> where)
|
||||
{
|
||||
await Repository.DeleteAll(ApiHelper.ParseWhere<T>(where));
|
||||
Expression<Func<T, bool>>? w = ApiHelper.ParseWhere<T>(where);
|
||||
if (w == null)
|
||||
return BadRequest(new RequestError("Incule a filter to delete items, all items won't be deleted."));
|
||||
|
||||
await Repository.DeleteAll(w);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
|
@ -59,14 +59,14 @@ namespace Kyoo.Core.Api
|
||||
|
||||
private async Task<IActionResult> _GetImage(Identifier identifier, string image, ImageQuality? quality)
|
||||
{
|
||||
T resource = await identifier.Match(
|
||||
T? resource = await identifier.Match(
|
||||
id => Repository.GetOrDefault(id),
|
||||
slug => Repository.GetOrDefault(slug)
|
||||
);
|
||||
if (resource == null)
|
||||
return NotFound();
|
||||
string path = _thumbs.GetImagePath(resource, image, quality ?? ImageQuality.High);
|
||||
if (path == null || !System.IO.File.Exists(path))
|
||||
if (!System.IO.File.Exists(path))
|
||||
return NotFound();
|
||||
|
||||
if (!identifier.Match(id => false, slug => slug == "random"))
|
||||
|
@ -42,7 +42,7 @@ namespace Kyoo.Core.Api
|
||||
/// <inheritdoc />
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (context.ActionArguments.TryGetValue("where", out object dic) && dic is Dictionary<string, string> where)
|
||||
if (context.ActionArguments.TryGetValue("where", out object? dic) && dic is Dictionary<string, string> where)
|
||||
{
|
||||
Dictionary<string, string> nWhere = new(where, StringComparer.InvariantCultureIgnoreCase);
|
||||
nWhere.Remove("fields");
|
||||
@ -55,7 +55,7 @@ namespace Kyoo.Core.Api
|
||||
}
|
||||
|
||||
List<string> fields = context.HttpContext.Request.Query["fields"]
|
||||
.SelectMany(x => x.Split(','))
|
||||
.SelectMany(x => x!.Split(','))
|
||||
.ToList();
|
||||
|
||||
if (context.ActionDescriptor is ControllerActionDescriptor descriptor)
|
||||
@ -77,7 +77,7 @@ namespace Kyoo.Core.Api
|
||||
fields = fields
|
||||
.Select(x =>
|
||||
{
|
||||
string property = properties
|
||||
string? property = properties
|
||||
.FirstOrDefault(y
|
||||
=> string.Equals(x, y.Name, StringComparison.InvariantCultureIgnoreCase))
|
||||
?.Name;
|
||||
@ -88,6 +88,7 @@ namespace Kyoo.Core.Api
|
||||
);
|
||||
return null;
|
||||
})
|
||||
.OfType<string>()
|
||||
.ToList();
|
||||
if (context.Result != null)
|
||||
return;
|
||||
@ -111,12 +112,12 @@ namespace Kyoo.Core.Api
|
||||
return;
|
||||
|
||||
ILibraryManager library = context.HttpContext.RequestServices.GetRequiredService<ILibraryManager>();
|
||||
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"];
|
||||
Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));
|
||||
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"]!;
|
||||
Type? pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));
|
||||
|
||||
if (pageType != null)
|
||||
{
|
||||
foreach (IResource resource in ((dynamic)result.Value).Items)
|
||||
foreach (IResource resource in ((dynamic)result.Value!).Items)
|
||||
{
|
||||
foreach (string field in fields!)
|
||||
await library.Load(resource, field);
|
||||
@ -125,7 +126,7 @@ namespace Kyoo.Core.Api
|
||||
else if (result.DeclaredType.IsAssignableTo(typeof(IResource)))
|
||||
{
|
||||
foreach (string field in fields!)
|
||||
await library.Load((IResource)result.Value, field);
|
||||
await library.Load((IResource)result.Value!, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,16 +53,16 @@ namespace Kyoo.Core.Api
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
LoadableRelationAttribute relation = member.GetCustomAttribute<LoadableRelationAttribute>();
|
||||
LoadableRelationAttribute? relation = member.GetCustomAttribute<LoadableRelationAttribute>();
|
||||
if (relation != null)
|
||||
{
|
||||
property.ShouldSerialize = _ =>
|
||||
{
|
||||
string resType = (string)_httpContextAccessor.HttpContext!.Items["ResourceType"];
|
||||
string resType = (string)_httpContextAccessor.HttpContext!.Items["ResourceType"]!;
|
||||
if (member.DeclaringType!.Name != resType)
|
||||
return false;
|
||||
ICollection<string> fields = (ICollection<string>)_httpContextAccessor.HttpContext!.Items["fields"];
|
||||
return fields!.Contains(member.Name);
|
||||
ICollection<string> fields = (ICollection<string>)_httpContextAccessor.HttpContext!.Items["fields"]!;
|
||||
return fields.Contains(member.Name);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -31,10 +31,16 @@ namespace Kyoo.Core.Api
|
||||
public class PeopleRoleConverter : JsonConverter<PeopleRole>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer)
|
||||
public override void WriteJson(JsonWriter writer, PeopleRole? value, JsonSerializer serializer)
|
||||
{
|
||||
ICollection<PeopleRole> oldPeople = value.Show?.People;
|
||||
ICollection<PeopleRole> oldRoles = value.People?.Roles;
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
ICollection<PeopleRole>? oldPeople = value.Show?.People;
|
||||
ICollection<PeopleRole>? oldRoles = value.People?.Roles;
|
||||
if (value.Show != null)
|
||||
value.Show.People = null;
|
||||
if (value.People != null)
|
||||
@ -54,7 +60,7 @@ namespace Kyoo.Core.Api
|
||||
/// <inheritdoc />
|
||||
public override PeopleRole ReadJson(JsonReader reader,
|
||||
Type objectType,
|
||||
PeopleRole existingValue,
|
||||
PeopleRole? existingValue,
|
||||
bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
Expression<Func<PeopleRole, bool>> whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
|
||||
Expression<Func<PeopleRole, bool>>? whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
|
||||
Sort<PeopleRole> sort = Sort<PeopleRole>.From(sortBy);
|
||||
|
||||
ICollection<PeopleRole> resources = await identifier.Match(
|
||||
|
@ -83,7 +83,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Show>(x => x.StudioId, x => x.Studio.Slug)),
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Show>(x => x.StudioId, x => x.Studio!.Slug)),
|
||||
Sort<Show>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
|
@ -84,7 +84,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Show> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Show, Collection>(x => x.Collections)),
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Show, Collection>(x => x.Collections!)),
|
||||
Sort<Show>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
|
@ -73,7 +73,7 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Show>> GetShow(Identifier identifier)
|
||||
{
|
||||
return await _libraryManager.Get(identifier.IsContainedIn<Show, Episode>(x => x.Episodes));
|
||||
return await _libraryManager.Get(identifier.IsContainedIn<Show, Episode>(x => x.Episodes!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -93,10 +93,10 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Season>> GetSeason(Identifier identifier)
|
||||
{
|
||||
Season ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn<Season, Episode>(x => x.Episodes));
|
||||
Season? ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn<Season, Episode>(x => x.Episodes!));
|
||||
if (ret != null)
|
||||
return ret;
|
||||
Episode episode = await identifier.Match(
|
||||
Episode? episode = await identifier.Match(
|
||||
id => _libraryManager.GetOrDefault<Episode>(id),
|
||||
slug => _libraryManager.GetOrDefault<Episode>(slug)
|
||||
);
|
||||
|
@ -108,7 +108,7 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Studio>> GetStudio(Identifier identifier)
|
||||
{
|
||||
return await _libraryManager.Get(identifier.IsContainedIn<Studio, Movie>(x => x.Movies));
|
||||
return await _libraryManager.Get(identifier.IsContainedIn<Studio, Movie>(x => x.Movies!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -136,7 +136,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Collection, Movie>(x => x.Movies)),
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Collection, Movie>(x => x.Movies!)),
|
||||
Sort<Collection>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
|
@ -84,7 +84,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season.Slug)),
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.SeasonId, x => x.Season!.Slug)),
|
||||
Sort<Episode>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
@ -109,7 +109,7 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Show>> GetShow(Identifier identifier)
|
||||
{
|
||||
Show ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn<Show, Season>(x => x.Seasons));
|
||||
Show? ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn<Show, Season>(x => x.Seasons!));
|
||||
if (ret == null)
|
||||
return NotFound();
|
||||
return ret;
|
||||
|
@ -86,7 +86,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Season> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Season>(x => x.ShowId, x => x.Show.Slug)),
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Season>(x => x.ShowId, x => x.Show!.Slug)),
|
||||
Sort<Season>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
@ -121,7 +121,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.ShowId, x => x.Show.Slug)),
|
||||
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.ShowId, x => x.Show!.Slug)),
|
||||
Sort<Episode>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
@ -155,7 +155,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Dictionary<string, string> where,
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
Expression<Func<PeopleRole, bool>> whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
|
||||
Expression<Func<PeopleRole, bool>>? whereQuery = ApiHelper.ParseWhere<PeopleRole>(where);
|
||||
Sort<PeopleRole> sort = Sort<PeopleRole>.From(sortBy);
|
||||
|
||||
ICollection<PeopleRole> resources = await identifier.Match(
|
||||
@ -180,7 +180,7 @@ namespace Kyoo.Core.Api
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Studio>> GetStudio(Identifier identifier)
|
||||
{
|
||||
return await _libraryManager.Get(identifier.IsContainedIn<Studio, Show>(x => x.Shows));
|
||||
return await _libraryManager.Get(identifier.IsContainedIn<Studio, Show>(x => x.Shows!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -208,7 +208,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Pagination pagination)
|
||||
{
|
||||
ICollection<Collection> resources = await _libraryManager.GetAll(
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Collection, Show>(x => x.Shows)),
|
||||
ApiHelper.ParseWhere(where, identifier.IsContainedIn<Collection, Show>(x => x.Shows!)),
|
||||
Sort<Collection>.From(sortBy),
|
||||
pagination
|
||||
);
|
||||
|
@ -180,8 +180,8 @@ namespace Kyoo.Postgresql
|
||||
modelBuilder.Entity<T>()
|
||||
.Property(x => x.ExternalId)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
|
||||
v => JsonSerializer.Deserialize<Dictionary<string, MetadataId>>(v, (JsonSerializerOptions)null)
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v => JsonSerializer.Deserialize<Dictionary<string, MetadataId>>(v, (JsonSerializerOptions?)null)!
|
||||
)
|
||||
.HasColumnType("json");
|
||||
}
|
||||
@ -215,8 +215,8 @@ namespace Kyoo.Postgresql
|
||||
/// <typeparam name="T">The owning type of the relationship</typeparam>
|
||||
/// <typeparam name="T2">The owned type of the relationship</typeparam>
|
||||
private void _HasManyToMany<T, T2>(ModelBuilder modelBuilder,
|
||||
Expression<Func<T, IEnumerable<T2>>> firstNavigation,
|
||||
Expression<Func<T2, IEnumerable<T>>> secondNavigation)
|
||||
Expression<Func<T, IEnumerable<T2>?>> firstNavigation,
|
||||
Expression<Func<T2, IEnumerable<T>?>> secondNavigation)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource
|
||||
{
|
||||
@ -360,7 +360,7 @@ namespace Kyoo.Postgresql
|
||||
public T GetTemporaryObject<T>(T model)
|
||||
where T : class, IResource
|
||||
{
|
||||
T tmp = Set<T>().Local.FirstOrDefault(x => x.Id == model.Id);
|
||||
T? tmp = Set<T>().Local.FirstOrDefault(x => x.Id == model.Id);
|
||||
if (tmp != null)
|
||||
return tmp;
|
||||
Entry(model).State = EntityState.Unchanged;
|
||||
@ -509,8 +509,7 @@ namespace Kyoo.Postgresql
|
||||
/// <param name="slug">The slug of the resource to check</param>
|
||||
/// <typeparam name="T">The type of entity to check</typeparam>
|
||||
/// <returns>The local entity representing the resource with the given slug if it exists or null.</returns>
|
||||
[CanBeNull]
|
||||
public T LocalEntity<T>(string slug)
|
||||
public T? LocalEntity<T>(string slug)
|
||||
where T : class, IResource
|
||||
{
|
||||
return ChangeTracker.Entries<T>()
|
||||
|
@ -106,7 +106,7 @@ namespace Kyoo.Postgresql
|
||||
modelBuilder.HasPostgresEnum<Genre>();
|
||||
modelBuilder.HasPostgresEnum<ItemKind>();
|
||||
|
||||
modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5)))
|
||||
modelBuilder.HasDbFunction(typeof(DatabaseContext).GetMethod(nameof(MD5))!)
|
||||
.HasTranslation(args =>
|
||||
new SqlFunctionExpression(
|
||||
"md5",
|
||||
|
Loading…
x
Reference in New Issue
Block a user