mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-01 04:34:50 -04:00
Cleanup ef repositories
This commit is contained in:
parent
69e8340c95
commit
1d553daeaf
@ -31,46 +31,20 @@ namespace Kyoo.Core.Controllers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A local repository to handle collections
|
/// A local repository to handle collections
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CollectionRepository : LocalRepository<Collection>
|
public class CollectionRepository(DatabaseContext database) : LocalRepository<Collection>(database)
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The database handle
|
|
||||||
/// </summary>
|
|
||||||
private readonly DatabaseContext _database;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="CollectionRepository"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="database">The database handle to use</param>
|
|
||||||
/// <param name="thumbs">The thumbnail manager used to store images.</param>
|
|
||||||
public CollectionRepository(DatabaseContext database, IThumbnailsManager thumbs)
|
|
||||||
: base(database, thumbs)
|
|
||||||
{
|
|
||||||
_database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Collection>> Search(
|
public override async Task<ICollection<Collection>> Search(
|
||||||
string query,
|
string query,
|
||||||
Include<Collection>? include = default
|
Include<Collection>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Collections, include)
|
return await AddIncludes(Database.Collections, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task<Collection> Create(Collection obj)
|
|
||||||
{
|
|
||||||
await base.Create(obj);
|
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
|
||||||
await _database.SaveChangesAsync(() => Get(obj.Slug));
|
|
||||||
await IRepository<Collection>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task Validate(Collection resource)
|
protected override async Task Validate(Collection resource)
|
||||||
{
|
{
|
||||||
@ -82,21 +56,13 @@ public class CollectionRepository : LocalRepository<Collection>
|
|||||||
|
|
||||||
public async Task AddMovie(Guid id, Guid movieId)
|
public async Task AddMovie(Guid id, Guid movieId)
|
||||||
{
|
{
|
||||||
_database.AddLinks<Collection, Movie>(id, movieId);
|
Database.AddLinks<Collection, Movie>(id, movieId);
|
||||||
await _database.SaveChangesAsync();
|
await Database.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddShow(Guid id, Guid showId)
|
public async Task AddShow(Guid id, Guid showId)
|
||||||
{
|
{
|
||||||
_database.AddLinks<Collection, Show>(id, showId);
|
Database.AddLinks<Collection, Show>(id, showId);
|
||||||
await _database.SaveChangesAsync();
|
await Database.SaveChangesAsync();
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task Delete(Collection obj)
|
|
||||||
{
|
|
||||||
_database.Entry(obj).State = EntityState.Deleted;
|
|
||||||
await _database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,8 @@ namespace Kyoo.Core.Controllers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A local repository to handle episodes.
|
/// A local repository to handle episodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EpisodeRepository(
|
public class EpisodeRepository(DatabaseContext database, IRepository<Show> shows)
|
||||||
DatabaseContext database,
|
: LocalRepository<Episode>(database)
|
||||||
IRepository<Show> shows,
|
|
||||||
IThumbnailsManager thumbs
|
|
||||||
) : LocalRepository<Episode>(database, thumbs)
|
|
||||||
{
|
{
|
||||||
static EpisodeRepository()
|
static EpisodeRepository()
|
||||||
{
|
{
|
||||||
@ -64,34 +61,18 @@ public class EpisodeRepository(
|
|||||||
Include<Episode>? include = default
|
Include<Episode>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(database.Episodes, include)
|
return await AddIncludes(Database.Episodes, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<Episode?> GetDuplicated(Episode item)
|
|
||||||
{
|
|
||||||
if (item is { SeasonNumber: not null, EpisodeNumber: not null })
|
|
||||||
return database.Episodes.FirstOrDefaultAsync(x =>
|
|
||||||
x.ShowId == item.ShowId
|
|
||||||
&& x.SeasonNumber == item.SeasonNumber
|
|
||||||
&& x.EpisodeNumber == item.EpisodeNumber
|
|
||||||
);
|
|
||||||
return database.Episodes.FirstOrDefaultAsync(x =>
|
|
||||||
x.ShowId == item.ShowId && x.AbsoluteNumber == item.AbsoluteNumber
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<Episode> Create(Episode obj)
|
public override async Task<Episode> Create(Episode obj)
|
||||||
{
|
{
|
||||||
|
// Set it for the OnResourceCreated event and the return value.
|
||||||
obj.ShowSlug = obj.Show?.Slug ?? (await shows.Get(obj.ShowId)).Slug;
|
obj.ShowSlug = obj.Show?.Slug ?? (await shows.Get(obj.ShowId)).Slug;
|
||||||
await base.Create(obj);
|
return await base.Create(obj);
|
||||||
database.Entry(obj).State = EntityState.Added;
|
|
||||||
await database.SaveChangesAsync(() => GetDuplicated(obj));
|
|
||||||
await IRepository<Episode>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -111,7 +92,7 @@ public class EpisodeRepository(
|
|||||||
}
|
}
|
||||||
if (resource.SeasonId == null && resource.SeasonNumber != null)
|
if (resource.SeasonId == null && resource.SeasonNumber != null)
|
||||||
{
|
{
|
||||||
resource.Season = await database.Seasons.FirstOrDefaultAsync(x =>
|
resource.Season = await Database.Seasons.FirstOrDefaultAsync(x =>
|
||||||
x.ShowId == resource.ShowId && x.SeasonNumber == resource.SeasonNumber
|
x.ShowId == resource.ShowId && x.SeasonNumber == resource.SeasonNumber
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -120,14 +101,40 @@ public class EpisodeRepository(
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task Delete(Episode obj)
|
public override async Task Delete(Episode obj)
|
||||||
{
|
{
|
||||||
int epCount = await database
|
int epCount = await Database
|
||||||
.Episodes.Where(x => x.ShowId == obj.ShowId)
|
.Episodes.Where(x => x.ShowId == obj.ShowId)
|
||||||
.Take(2)
|
.Take(2)
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
database.Entry(obj).State = EntityState.Deleted;
|
|
||||||
await database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
if (epCount == 1)
|
if (epCount == 1)
|
||||||
await shows.Delete(obj.ShowId);
|
await shows.Delete(obj.ShowId);
|
||||||
|
else
|
||||||
|
await base.Delete(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task DeleteAll(Filter<Episode> filter)
|
||||||
|
{
|
||||||
|
ICollection<Episode> items = await GetAll(filter);
|
||||||
|
Guid[] ids = items.Select(x => x.Id).ToArray();
|
||||||
|
|
||||||
|
await Database.Set<Episode>().Where(x => ids.Contains(x.Id)).ExecuteDeleteAsync();
|
||||||
|
foreach (Episode resource in items)
|
||||||
|
await IRepository<Episode>.OnResourceDeleted(resource);
|
||||||
|
|
||||||
|
Guid[] showIds = await Database
|
||||||
|
.Set<Episode>()
|
||||||
|
.Where(filter.ToEfLambda())
|
||||||
|
.Select(x => x.Show!)
|
||||||
|
.Where(x => !x.Episodes!.Any())
|
||||||
|
.Select(x => x.Id)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
if (!showIds.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Filter<Show>[] showFilters = showIds
|
||||||
|
.Select(x => new Filter<Show>.Eq(nameof(Show.Id), x))
|
||||||
|
.ToArray();
|
||||||
|
await shows.DeleteAll(Filter.Or(showFilters)!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,38 +29,14 @@ using Kyoo.Abstractions.Models.Attributes;
|
|||||||
using Kyoo.Abstractions.Models.Exceptions;
|
using Kyoo.Abstractions.Models.Exceptions;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Postgresql;
|
using Kyoo.Postgresql;
|
||||||
using Kyoo.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers;
|
namespace Kyoo.Core.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
public abstract class LocalRepository<T>(DatabaseContext database) : IRepository<T>
|
||||||
/// A base class to create repositories using Entity Framework.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of this repository</typeparam>
|
|
||||||
public abstract class LocalRepository<T> : IRepository<T>
|
|
||||||
where T : class, IResource, IQuery
|
where T : class, IResource, IQuery
|
||||||
{
|
{
|
||||||
/// <summary>
|
public DatabaseContext Database => database;
|
||||||
/// The Entity Framework's Database handle.
|
|
||||||
/// </summary>
|
|
||||||
protected DbContext Database { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The thumbnail manager used to store images.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IThumbnailsManager _thumbs;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new base <see cref="LocalRepository{T}"/> with the given database handle.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="database">A database connection to load resources of type <typeparamref name="T"/></param>
|
|
||||||
/// <param name="thumbs">The thumbnail manager used to store images.</param>
|
|
||||||
protected LocalRepository(DbContext database, IThumbnailsManager thumbs)
|
|
||||||
{
|
|
||||||
Database = database;
|
|
||||||
_thumbs = thumbs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Type RepositoryType => typeof(T);
|
public Type RepositoryType => typeof(T);
|
||||||
@ -127,12 +103,6 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a resource from it's ID and make the <see cref="Database"/> instance track it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The ID of the resource</param>
|
|
||||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
|
||||||
/// <returns>The tracked resource with the given ID</returns>
|
|
||||||
protected virtual async Task<T> GetWithTracking(Guid id)
|
protected virtual async Task<T> GetWithTracking(Guid 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);
|
||||||
@ -174,11 +144,6 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Task<T?> GetDuplicated(T item)
|
|
||||||
{
|
|
||||||
return GetOrDefault(item.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual Task<T?> GetOrDefault(Guid id, Include<T>? include = default)
|
public virtual Task<T?> GetOrDefault(Guid id, Include<T>? include = default)
|
||||||
{
|
{
|
||||||
@ -303,26 +268,9 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
public virtual async Task<T> Create(T obj)
|
public virtual async Task<T> Create(T obj)
|
||||||
{
|
{
|
||||||
await Validate(obj);
|
await Validate(obj);
|
||||||
if (obj is IThumbnails thumbs)
|
Database.Entry(obj).State = EntityState.Added;
|
||||||
{
|
await Database.SaveChangesAsync(() => Get(obj.Slug));
|
||||||
try
|
await IRepository<T>.OnResourceCreated(obj);
|
||||||
{
|
|
||||||
await _thumbs.DownloadImages(thumbs);
|
|
||||||
}
|
|
||||||
catch (DuplicatedItemException e) when (e.Existing is null)
|
|
||||||
{
|
|
||||||
throw new DuplicatedItemException(await GetDuplicated(obj));
|
|
||||||
}
|
|
||||||
if (thumbs.Poster != null)
|
|
||||||
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;
|
|
||||||
if (thumbs.Logo != null)
|
|
||||||
Database.Entry(thumbs).Reference(x => x.Logo).TargetEntry!.State =
|
|
||||||
EntityState.Added;
|
|
||||||
}
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,27 +294,11 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<T> Edit(T edited)
|
public virtual async Task<T> Edit(T edited)
|
||||||
{
|
{
|
||||||
bool lazyLoading = Database.ChangeTracker.LazyLoadingEnabled;
|
await Validate(edited);
|
||||||
Database.ChangeTracker.LazyLoadingEnabled = false;
|
Database.Entry(edited).State = EntityState.Modified;
|
||||||
try
|
await Database.SaveChangesAsync();
|
||||||
{
|
await IRepository<T>.OnResourceEdited(edited);
|
||||||
T old = await GetWithTracking(edited.Id);
|
return edited;
|
||||||
|
|
||||||
Merger.Complete(
|
|
||||||
old,
|
|
||||||
edited,
|
|
||||||
x => x.GetCustomAttribute<LoadableRelationAttribute>() == null
|
|
||||||
);
|
|
||||||
await EditRelations(old, edited);
|
|
||||||
await Database.SaveChangesAsync();
|
|
||||||
await IRepository<T>.OnResourceEdited(old);
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
|
|
||||||
Database.ChangeTracker.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -391,39 +323,9 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An overridable method to edit relation of a resource.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="resource">
|
|
||||||
/// The non edited resource
|
|
||||||
/// </param>
|
|
||||||
/// <param name="changed">
|
|
||||||
/// The new version of <paramref name="resource"/>.
|
|
||||||
/// This item will be saved on the database and replace <paramref name="resource"/>
|
|
||||||
/// </param>
|
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
|
||||||
protected virtual Task EditRelations(T resource, T changed)
|
|
||||||
{
|
|
||||||
if (resource is IThumbnails thumbs && changed is IThumbnails chng)
|
|
||||||
{
|
|
||||||
Database.Entry(thumbs).Reference(x => x.Poster).IsModified =
|
|
||||||
thumbs.Poster != chng.Poster;
|
|
||||||
Database.Entry(thumbs).Reference(x => x.Thumbnail).IsModified =
|
|
||||||
thumbs.Thumbnail != chng.Thumbnail;
|
|
||||||
Database.Entry(thumbs).Reference(x => x.Logo).IsModified = thumbs.Logo != chng.Logo;
|
|
||||||
}
|
|
||||||
return Validate(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A method called just before saving a new resource to the database.
|
|
||||||
/// It is also called on the default implementation of <see cref="EditRelations"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="resource">The resource that will be saved</param>
|
|
||||||
/// <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 (
|
if (
|
||||||
@ -433,25 +335,8 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (string.IsNullOrEmpty(resource.Slug))
|
if (string.IsNullOrEmpty(resource.Slug))
|
||||||
throw new ArgumentException("Resource can't have null as a slug.");
|
throw new ArgumentException("Resource can't have null as a slug.");
|
||||||
if (int.TryParse(resource.Slug, out int _) || resource.Slug == "random")
|
if (resource.Slug == "random")
|
||||||
{
|
throw new ArgumentException("Resources slug can't be the literal \"random\".");
|
||||||
try
|
|
||||||
{
|
|
||||||
MethodInfo? setter = typeof(T).GetProperty(nameof(resource.Slug))!.GetSetMethod();
|
|
||||||
if (setter != null)
|
|
||||||
setter.Invoke(resource, new object[] { resource.Slug + '!' });
|
|
||||||
else
|
|
||||||
throw new ArgumentException(
|
|
||||||
"Resources slug can't be number only or the literal \"random\"."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
|
||||||
"Resources slug can't be number only or the literal \"random\"."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,18 +355,20 @@ public abstract class LocalRepository<T> : IRepository<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual Task Delete(T obj)
|
public virtual async Task Delete(T obj)
|
||||||
{
|
{
|
||||||
IRepository<T>.OnResourceDeleted(obj);
|
await Database.Set<T>().Where(x => x.Id == obj.Id).ExecuteDeleteAsync();
|
||||||
if (obj is IThumbnails thumbs)
|
await IRepository<T>.OnResourceDeleted(obj);
|
||||||
return _thumbs.DeleteImages(thumbs);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task DeleteAll(Filter<T> filter)
|
public virtual async Task DeleteAll(Filter<T> filter)
|
||||||
{
|
{
|
||||||
foreach (T resource in await GetAll(filter))
|
ICollection<T> items = await GetAll(filter);
|
||||||
await Delete(resource);
|
Guid[] ids = items.Select(x => x.Id).ToArray();
|
||||||
|
await Database.Set<T>().Where(x => ids.Contains(x.Id)).ExecuteDeleteAsync();
|
||||||
|
|
||||||
|
foreach (T resource in items)
|
||||||
|
await IRepository<T>.OnResourceDeleted(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,82 +27,29 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace Kyoo.Core.Controllers;
|
namespace Kyoo.Core.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
public class MovieRepository(DatabaseContext database, IRepository<Studio> studios)
|
||||||
/// A local repository to handle shows
|
: LocalRepository<Movie>(database)
|
||||||
/// </summary>
|
|
||||||
public class MovieRepository : LocalRepository<Movie>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The database handle
|
|
||||||
/// </summary>
|
|
||||||
private readonly DatabaseContext _database;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A studio repository to handle creation/validation of related studios.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IRepository<Studio> _studios;
|
|
||||||
|
|
||||||
public MovieRepository(
|
|
||||||
DatabaseContext database,
|
|
||||||
IRepository<Studio> studios,
|
|
||||||
IThumbnailsManager thumbs
|
|
||||||
)
|
|
||||||
: base(database, thumbs)
|
|
||||||
{
|
|
||||||
_database = database;
|
|
||||||
_studios = studios;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Movie>> Search(
|
public override async Task<ICollection<Movie>> Search(
|
||||||
string query,
|
string query,
|
||||||
Include<Movie>? include = default
|
Include<Movie>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Movies, include)
|
return await AddIncludes(Database.Movies, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task<Movie> Create(Movie obj)
|
|
||||||
{
|
|
||||||
await base.Create(obj);
|
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
|
||||||
await _database.SaveChangesAsync(() => Get(obj.Slug));
|
|
||||||
await IRepository<Movie>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task Validate(Movie resource)
|
protected override async Task Validate(Movie resource)
|
||||||
{
|
{
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
if (resource.Studio != null)
|
if (resource.Studio != null)
|
||||||
{
|
{
|
||||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
resource.Studio = await studios.CreateIfNotExists(resource.Studio);
|
||||||
resource.StudioId = resource.Studio.Id;
|
resource.StudioId = resource.Studio.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override async Task EditRelations(Movie resource, Movie changed)
|
|
||||||
{
|
|
||||||
await Validate(changed);
|
|
||||||
|
|
||||||
if (changed.Studio != null || changed.StudioId == null)
|
|
||||||
{
|
|
||||||
await Database.Entry(resource).Reference(x => x.Studio).LoadAsync();
|
|
||||||
resource.Studio = changed.Studio;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task Delete(Movie obj)
|
|
||||||
{
|
|
||||||
_database.Remove(obj);
|
|
||||||
await _database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -31,16 +31,8 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace Kyoo.Core.Controllers;
|
namespace Kyoo.Core.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
public class SeasonRepository(DatabaseContext database) : LocalRepository<Season>(database)
|
||||||
/// A local repository to handle seasons.
|
|
||||||
/// </summary>
|
|
||||||
public class SeasonRepository : LocalRepository<Season>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The database handle
|
|
||||||
/// </summary>
|
|
||||||
private readonly DatabaseContext _database;
|
|
||||||
|
|
||||||
static SeasonRepository()
|
static SeasonRepository()
|
||||||
{
|
{
|
||||||
// Edit seasons slugs when the show's slug changes.
|
// Edit seasons slugs when the show's slug changes.
|
||||||
@ -61,31 +53,13 @@ public class SeasonRepository : LocalRepository<Season>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="SeasonRepository"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="database">The database handle that will be used</param>
|
|
||||||
/// <param name="thumbs">The thumbnail manager used to store images.</param>
|
|
||||||
public SeasonRepository(DatabaseContext database, IThumbnailsManager thumbs)
|
|
||||||
: base(database, thumbs)
|
|
||||||
{
|
|
||||||
_database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task<Season?> GetDuplicated(Season item)
|
|
||||||
{
|
|
||||||
return _database.Seasons.FirstOrDefaultAsync(x =>
|
|
||||||
x.ShowId == item.ShowId && x.SeasonNumber == item.SeasonNumber
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<ICollection<Season>> Search(
|
public override async Task<ICollection<Season>> Search(
|
||||||
string query,
|
string query,
|
||||||
Include<Season>? include = default
|
Include<Season>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Seasons, include)
|
return await AddIncludes(Database.Seasons, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name!, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@ -94,14 +68,11 @@ public class SeasonRepository : LocalRepository<Season>
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<Season> Create(Season obj)
|
public override async Task<Season> Create(Season obj)
|
||||||
{
|
{
|
||||||
await base.Create(obj);
|
// Set it for the OnResourceCreated event and the return value.
|
||||||
obj.ShowSlug =
|
obj.ShowSlug =
|
||||||
(await _database.Shows.FirstOrDefaultAsync(x => x.Id == obj.ShowId))?.Slug
|
(await Database.Shows.FirstOrDefaultAsync(x => x.Id == obj.ShowId))?.Slug
|
||||||
?? throw new ItemNotFoundException($"No show found with ID {obj.ShowId}");
|
?? throw new ItemNotFoundException($"No show found with ID {obj.ShowId}");
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
return await base.Create(obj);
|
||||||
await _database.SaveChangesAsync(() => GetDuplicated(obj));
|
|
||||||
await IRepository<Season>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@ -120,12 +91,4 @@ public class SeasonRepository : LocalRepository<Season>
|
|||||||
resource.ShowId = resource.Show.Id;
|
resource.ShowId = resource.Show.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override async Task Delete(Season obj)
|
|
||||||
{
|
|
||||||
_database.Remove(obj);
|
|
||||||
await _database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,87 +23,33 @@ using Kyoo.Abstractions.Controllers;
|
|||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Postgresql;
|
using Kyoo.Postgresql;
|
||||||
using Kyoo.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers;
|
namespace Kyoo.Core.Controllers;
|
||||||
|
|
||||||
/// <summary>
|
public class ShowRepository(DatabaseContext database, IRepository<Studio> studios)
|
||||||
/// A local repository to handle shows
|
: LocalRepository<Show>(database)
|
||||||
/// </summary>
|
|
||||||
public class ShowRepository : LocalRepository<Show>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The database handle
|
|
||||||
/// </summary>
|
|
||||||
private readonly DatabaseContext _database;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A studio repository to handle creation/validation of related studios.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IRepository<Studio> _studios;
|
|
||||||
|
|
||||||
public ShowRepository(
|
|
||||||
DatabaseContext database,
|
|
||||||
IRepository<Studio> studios,
|
|
||||||
IThumbnailsManager thumbs
|
|
||||||
)
|
|
||||||
: base(database, thumbs)
|
|
||||||
{
|
|
||||||
_database = database;
|
|
||||||
_studios = studios;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Show>> Search(
|
public override async Task<ICollection<Show>> Search(
|
||||||
string query,
|
string query,
|
||||||
Include<Show>? include = default
|
Include<Show>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Shows, include)
|
return await AddIncludes(Database.Shows, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name + " " + x.Slug, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task<Show> Create(Show obj)
|
|
||||||
{
|
|
||||||
await base.Create(obj);
|
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
|
||||||
await _database.SaveChangesAsync(() => Get(obj.Slug));
|
|
||||||
await IRepository<Show>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override async Task Validate(Show resource)
|
protected override async Task Validate(Show resource)
|
||||||
{
|
{
|
||||||
await base.Validate(resource);
|
await base.Validate(resource);
|
||||||
if (resource.Studio != null)
|
if (resource.Studio != null)
|
||||||
{
|
{
|
||||||
resource.Studio = await _studios.CreateIfNotExists(resource.Studio);
|
resource.Studio = await studios.CreateIfNotExists(resource.Studio);
|
||||||
resource.StudioId = resource.Studio.Id;
|
resource.StudioId = resource.Studio.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override async Task EditRelations(Show resource, Show changed)
|
|
||||||
{
|
|
||||||
await Validate(changed);
|
|
||||||
|
|
||||||
if (changed.Studio != null || changed.StudioId == null)
|
|
||||||
{
|
|
||||||
await Database.Entry(resource).Reference(x => x.Studio).LoadAsync();
|
|
||||||
resource.Studio = changed.Studio;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task Delete(Show obj)
|
|
||||||
{
|
|
||||||
_database.Remove(obj);
|
|
||||||
await _database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Postgresql;
|
using Kyoo.Postgresql;
|
||||||
using Kyoo.Utils;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Kyoo.Core.Controllers;
|
namespace Kyoo.Core.Controllers;
|
||||||
@ -31,51 +29,17 @@ namespace Kyoo.Core.Controllers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A local repository to handle studios
|
/// A local repository to handle studios
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StudioRepository : LocalRepository<Studio>
|
public class StudioRepository(DatabaseContext database) : LocalRepository<Studio>(database)
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The database handle
|
|
||||||
/// </summary>
|
|
||||||
private readonly DatabaseContext _database;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="StudioRepository"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="database">The database handle</param>
|
|
||||||
/// <param name="thumbs">The thumbnail manager used to store images.</param>
|
|
||||||
public StudioRepository(DatabaseContext database, IThumbnailsManager thumbs)
|
|
||||||
: base(database, thumbs)
|
|
||||||
{
|
|
||||||
_database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<Studio>> Search(
|
public override async Task<ICollection<Studio>> Search(
|
||||||
string query,
|
string query,
|
||||||
Include<Studio>? include = default
|
Include<Studio>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(_database.Studios, include)
|
return await AddIncludes(Database.Studios, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Name, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task<Studio> Create(Studio obj)
|
|
||||||
{
|
|
||||||
await base.Create(obj);
|
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
|
||||||
await _database.SaveChangesAsync(() => Get(obj.Slug));
|
|
||||||
await IRepository<Studio>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task Delete(Studio obj)
|
|
||||||
{
|
|
||||||
_database.Entry(obj).State = EntityState.Deleted;
|
|
||||||
await _database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,8 @@ public class UserRepository(
|
|||||||
DatabaseContext database,
|
DatabaseContext database,
|
||||||
DbConnection db,
|
DbConnection db,
|
||||||
SqlVariableContext context,
|
SqlVariableContext context,
|
||||||
IThumbnailsManager thumbs,
|
|
||||||
PermissionOption options
|
PermissionOption options
|
||||||
) : LocalRepository<User>(database, thumbs), IUserRepository
|
) : LocalRepository<User>(database), IUserRepository
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<ICollection<User>> Search(
|
public override async Task<ICollection<User>> Search(
|
||||||
@ -50,7 +49,7 @@ public class UserRepository(
|
|||||||
Include<User>? include = default
|
Include<User>? include = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await AddIncludes(database.Users, include)
|
return await AddIncludes(Database.Users, include)
|
||||||
.Where(x => EF.Functions.ILike(x.Username, $"%{query}%"))
|
.Where(x => EF.Functions.ILike(x.Username, $"%{query}%"))
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@ -60,26 +59,14 @@ public class UserRepository(
|
|||||||
public override async Task<User> Create(User obj)
|
public override async Task<User> Create(User obj)
|
||||||
{
|
{
|
||||||
// If no users exists, the new one will be an admin. Give it every permissions.
|
// If no users exists, the new one will be an admin. Give it every permissions.
|
||||||
if (!await database.Users.AnyAsync())
|
if (!await Database.Users.AnyAsync())
|
||||||
obj.Permissions = PermissionOption.Admin;
|
obj.Permissions = PermissionOption.Admin;
|
||||||
else if (!options.RequireVerification)
|
else if (!options.RequireVerification)
|
||||||
obj.Permissions = options.NewUser;
|
obj.Permissions = options.NewUser;
|
||||||
else
|
else
|
||||||
obj.Permissions = Array.Empty<string>();
|
obj.Permissions = Array.Empty<string>();
|
||||||
|
|
||||||
await base.Create(obj);
|
return await base.Create(obj);
|
||||||
database.Entry(obj).State = EntityState.Added;
|
|
||||||
await database.SaveChangesAsync(() => Get(obj.Slug));
|
|
||||||
await IRepository<User>.OnResourceCreated(obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override async Task Delete(User obj)
|
|
||||||
{
|
|
||||||
database.Entry(obj).State = EntityState.Deleted;
|
|
||||||
await database.SaveChangesAsync();
|
|
||||||
await base.Delete(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<User?> GetByExternalId(string provider, string id)
|
public Task<User?> GetByExternalId(string provider, string id)
|
||||||
@ -109,8 +96,8 @@ public class UserRepository(
|
|||||||
User user = await GetWithTracking(userId);
|
User user = await GetWithTracking(userId);
|
||||||
user.ExternalId[provider] = token;
|
user.ExternalId[provider] = token;
|
||||||
// without that, the change tracker does not find the modification. /shrug
|
// without that, the change tracker does not find the modification. /shrug
|
||||||
database.Entry(user).Property(x => x.ExternalId).IsModified = true;
|
Database.Entry(user).Property(x => x.ExternalId).IsModified = true;
|
||||||
await database.SaveChangesAsync();
|
await Database.SaveChangesAsync();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,8 +106,8 @@ public class UserRepository(
|
|||||||
User user = await GetWithTracking(userId);
|
User user = await GetWithTracking(userId);
|
||||||
user.ExternalId.Remove(provider);
|
user.ExternalId.Remove(provider);
|
||||||
// without that, the change tracker does not find the modification. /shrug
|
// without that, the change tracker does not find the modification. /shrug
|
||||||
database.Entry(user).Property(x => x.ExternalId).IsModified = true;
|
Database.Entry(user).Property(x => x.ExternalId).IsModified = true;
|
||||||
await database.SaveChangesAsync();
|
await Database.SaveChangesAsync();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using AspNetCore.Proxy;
|
using AspNetCore.Proxy;
|
||||||
|
@ -16,9 +16,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Meilisearch;
|
using Meilisearch;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user