diff --git a/Kyoo.Common/Controllers/ILibraryManager.cs b/Kyoo.Common/Controllers/ILibraryManager.cs index c627d715..52acb660 100644 --- a/Kyoo.Common/Controllers/ILibraryManager.cs +++ b/Kyoo.Common/Controllers/ILibraryManager.cs @@ -12,6 +12,7 @@ namespace Kyoo.Controllers { // Repositories ILibraryRepository LibraryRepository { get; } + ILibraryItemRepository LibraryItemRepository { get; } ICollectionRepository CollectionRepository { get; } IShowRepository ShowRepository { get; } ISeasonRepository SeasonRepository { get; } @@ -231,6 +232,28 @@ namespace Kyoo.Controllers Expression> sort, Pagination limit = default ) => GetCollectionsFromLibrary(showSlug, where, new Sort(sort), limit); + + Task> GetItemsFromLibrary(int id, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + + Task> GetItemsFromLibrary(int id, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetItemsFromLibrary(id, where, new Sort(sort), limit); + + Task> GetItemsFromLibrary(string librarySlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + + Task> GetItemsFromLibrary(string librarySlug, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetItemsFromLibrary(librarySlug, where, new Sort(sort), limit); // Helpers diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index 4cde8d27..cdcc9359 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -257,6 +257,31 @@ namespace Kyoo.Controllers ) => GetFromShow(showSlug, where, new Sort(sort), limit); } + public interface ILibraryItemRepository : IRepository + { + public Task> GetFromLibrary(int id, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + + public Task> GetFromLibrary(int id, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetFromLibrary(id, where, new Sort(sort), limit); + + public Task> GetFromLibrary(string librarySlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + + public Task> GetFromLibrary(string librarySlug, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetFromLibrary(librarySlug, where, new Sort(sort), limit); + } + public interface ICollectionRepository : IRepository { Task> GetFromShow(int showID, diff --git a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs index d9ffc4d0..fd6c2e79 100644 --- a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs +++ b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs @@ -9,6 +9,7 @@ namespace Kyoo.Controllers public class LibraryManager : ILibraryManager { public ILibraryRepository LibraryRepository { get; } + public ILibraryItemRepository LibraryItemRepository { get; } public ICollectionRepository CollectionRepository { get; } public IShowRepository ShowRepository { get; } public ISeasonRepository SeasonRepository { get; } @@ -20,6 +21,7 @@ namespace Kyoo.Controllers public IProviderRepository ProviderRepository { get; } public LibraryManager(ILibraryRepository libraryRepository, + ILibraryItemRepository libraryItemRepository, ICollectionRepository collectionRepository, IShowRepository showRepository, ISeasonRepository seasonRepository, @@ -31,6 +33,7 @@ namespace Kyoo.Controllers IPeopleRepository peopleRepository) { LibraryRepository = libraryRepository; + LibraryItemRepository = libraryItemRepository; CollectionRepository = collectionRepository; ShowRepository = showRepository; SeasonRepository = seasonRepository; @@ -361,6 +364,22 @@ namespace Kyoo.Controllers return CollectionRepository.GetFromLibrary(slug, where, sort, limit); } + public Task> GetItemsFromLibrary(int id, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return LibraryItemRepository.GetFromLibrary(id, where, sort, limit); + } + + public Task> GetItemsFromLibrary(string librarySlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return LibraryItemRepository.GetFromLibrary(librarySlug, where, sort, limit); + } + public Task AddShowLink(int showID, int? libraryID, int? collectionID) { return ShowRepository.AddShowLink(showID, libraryID, collectionID); diff --git a/Kyoo.Common/Models/LibraryItem.cs b/Kyoo.Common/Models/Ressources/LibraryItem.cs similarity index 100% rename from Kyoo.Common/Models/LibraryItem.cs rename to Kyoo.Common/Models/Ressources/LibraryItem.cs diff --git a/Kyoo.CommonAPI/LocalRepository.cs b/Kyoo.CommonAPI/LocalRepository.cs index 1842efe1..56381b6e 100644 --- a/Kyoo.CommonAPI/LocalRepository.cs +++ b/Kyoo.CommonAPI/LocalRepository.cs @@ -45,7 +45,7 @@ namespace Kyoo.Controllers public abstract Task> Search(string query); - public Task> GetAll(Expression> where = null, + public virtual Task> GetAll(Expression> where = null, Sort sort = default, Pagination limit = default) { diff --git a/Kyoo/Controllers/Repositories/LibraryItemRepository.cs b/Kyoo/Controllers/Repositories/LibraryItemRepository.cs new file mode 100644 index 00000000..92a029a2 --- /dev/null +++ b/Kyoo/Controllers/Repositories/LibraryItemRepository.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Kyoo.Models; +using Kyoo.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Controllers +{ + public class LibraryItemRepository : LocalRepository, ILibraryItemRepository + { + private readonly DatabaseContext _database; + private readonly IProviderRepository _providers; + private readonly Lazy _libraries; + private readonly Lazy _shows; + private readonly Lazy _collections; + protected override Expression> DefaultSort => x => x.Title; + + + public LibraryItemRepository(DatabaseContext database, IProviderRepository providers, IServiceProvider services) + : base(database) + { + _database = database; + _providers = providers; + _libraries = new Lazy(services.GetRequiredService); + _shows = new Lazy(services.GetRequiredService); + _collections = new Lazy(services.GetRequiredService); + } + + public override void Dispose() + { + _database.Dispose(); + _providers.Dispose(); + if (_shows.IsValueCreated) + _shows.Value.Dispose(); + if (_collections.IsValueCreated) + _collections.Value.Dispose(); + } + + public override async ValueTask DisposeAsync() + { + await _database.DisposeAsync(); + await _providers.DisposeAsync(); + if (_shows.IsValueCreated) + await _shows.Value.DisposeAsync(); + if (_collections.IsValueCreated) + await _collections.Value.DisposeAsync(); + } + + public override async Task Get(int id) + { + return id > 0 + ? new LibraryItem(await _shows.Value.Get(id)) + : new LibraryItem(await _collections.Value.Get(-id)); + } + + public override Task Get(string slug) + { + throw new InvalidOperationException(); + } + + private IQueryable ItemsQuery + => _database.Shows + .Where(x => !_database.CollectionLinks.Any(y => y.ShowID == x.ID)) + .Select(LibraryItem.FromShow) + .Concat(_database.Collections + .Select(LibraryItem.FromCollection)); + + public override Task> GetAll(Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return ApplyFilters(ItemsQuery, where, sort, limit); + } + + public override async Task> Search(string query) + { + return await ItemsQuery + .Where(x => EF.Functions.ILike(x.Title, $"%{query}%")) + .Take(20) + .ToListAsync(); + } + + public override Task Create(LibraryItem obj) => throw new InvalidOperationException(); + public override Task CreateIfNotExists(LibraryItem obj) => throw new InvalidOperationException(); + public override Task Edit(LibraryItem obj, bool reset) => throw new InvalidOperationException(); + protected override Task Validate(LibraryItem obj) => throw new InvalidOperationException(); + public override Task Delete(int id) => throw new InvalidOperationException(); + public override Task Delete(string slug) => throw new InvalidOperationException(); + public override Task Delete(LibraryItem obj) => throw new InvalidOperationException(); + + private IQueryable LibraryRelatedQuery(Expression> selector) + => _database.LibraryLinks + .Where(selector) + .Select(x => x.Show) + .Where(x => x != null) + .Where(x => !_database.CollectionLinks.Any(y => y.ShowID == x.ID)) + .Select(LibraryItem.FromShow) + .Concat(_database.LibraryLinks + .Where(selector) + .Select(x => x.Collection) + .Where(x => x != null) + .Select(LibraryItem.FromCollection)); + + public async Task> GetFromLibrary(int id, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + ICollection items = await ApplyFilters(LibraryRelatedQuery(x => x.LibraryID == id), + where, + sort, + limit); + if (!items.Any() && await _libraries.Value.Get(id) == null) + throw new ItemNotFound(); + return items; + } + + public async Task> GetFromLibrary(string slug, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + ICollection items = await ApplyFilters(LibraryRelatedQuery(x => x.Library.Slug == slug), + where, + sort, + limit); + if (!items.Any() && await _libraries.Value.Get(slug) == null) + throw new ItemNotFound(); + return items; + } + } +} \ No newline at end of file diff --git a/Kyoo/Controllers/Repositories/LibraryRepository.cs b/Kyoo/Controllers/Repositories/LibraryRepository.cs index e4718b76..5df3c071 100644 --- a/Kyoo/Controllers/Repositories/LibraryRepository.cs +++ b/Kyoo/Controllers/Repositories/LibraryRepository.cs @@ -136,29 +136,5 @@ namespace Kyoo.Controllers throw new ItemNotFound(); return libraries; } - - public async Task> GetItems(string librarySlug, - Expression> where = null, - Sort sort = default, - Pagination limit = default) - { - IQueryable query = _database.Shows - .Where(x => !_database.CollectionLinks.Any(y => y.ShowID == x.ID)) - .Select(LibraryItem.FromShow) - .Concat(_database.Collections - .Select(LibraryItem.FromCollection)); - - ICollection items = await ApplyFilters(query, - async id => id > 0 - ? new LibraryItem(await _database.Shows.FirstOrDefaultAsync(x => x.ID == id)) - : new LibraryItem(await _database.Collections.FirstOrDefaultAsync(x => x.ID == -id)), - x => x.Slug, - where, - sort, - limit); - if (!items.Any() && await Get(librarySlug) == null) - throw new ItemNotFound(); - return items; - } } } \ No newline at end of file diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index cb160a92..5e695133 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -47,8 +47,8 @@ namespace Kyoo { options.UseLazyLoadingProxies() .UseNpgsql(_configuration.GetConnectionString("Database")) - .EnableSensitiveDataLogging() - .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())); + .EnableSensitiveDataLogging() + .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole())); }, ServiceLifetime.Transient); services.AddDbContext(options => @@ -134,6 +134,7 @@ namespace Kyoo services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/Kyoo/Views/API/LibrariesApi.cs b/Kyoo/Views/API/LibrariesApi.cs index 689e71e0..d7786348 100644 --- a/Kyoo/Views/API/LibrariesApi.cs +++ b/Kyoo/Views/API/LibrariesApi.cs @@ -44,7 +44,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 50) { - where.Remove("id"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -77,7 +76,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 20) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -110,7 +108,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 50) { - where.Remove("id"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -143,7 +140,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 20) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -176,14 +172,45 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 50) { - where.Remove("id"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); try { - ICollection ressources = await ((LibraryRepository)_libraryManager.LibraryRepository).GetItems("", + ICollection ressources = await _libraryManager.GetItemsFromLibrary(id, + ApiHelper.ParseWhere(where), + new Sort(sortBy), + new Pagination(limit, afterID)); + + return Page(ressources, limit); + } + catch (ItemNotFound) + { + return NotFound(); + } + catch (ArgumentException ex) + { + return BadRequest(new {Error = ex.Message}); + } + } + + [HttpGet("{slug}/item")] + [HttpGet("{slug}/items")] + [Authorize(Policy = "Read")] + public async Task>> GetItems(string slug, + [FromQuery] string sortBy, + [FromQuery] int afterID, + [FromQuery] Dictionary where, + [FromQuery] int limit = 50) + { + where.Remove("sortBy"); + where.Remove("limit"); + where.Remove("afterID"); + + try + { + ICollection ressources = await _libraryManager.GetItemsFromLibrary(slug, ApiHelper.ParseWhere(where), new Sort(sortBy), new Pagination(limit, afterID)); @@ -199,38 +226,5 @@ namespace Kyoo.Api return BadRequest(new {Error = ex.Message}); } } - // - // [HttpGet("{slug}/collection")] - // [HttpGet("{slug}/collections")] - // [Authorize(Policy = "Read")] - // public async Task>> GetCollections(string slug, - // [FromQuery] string sortBy, - // [FromQuery] int afterID, - // [FromQuery] Dictionary where, - // [FromQuery] int limit = 20) - // { - // where.Remove("slug"); - // where.Remove("sortBy"); - // where.Remove("limit"); - // where.Remove("afterID"); - // - // try - // { - // ICollection ressources = await _libraryManager.GetCollectionsFromLibrary(slug, - // ApiHelper.ParseWhere(where), - // new Sort(sortBy), - // new Pagination(limit, afterID)); - // - // return Page(ressources, limit); - // } - // catch (ItemNotFound) - // { - // return NotFound(); - // } - // catch (ArgumentException ex) - // { - // return BadRequest(new {Error = ex.Message}); - // } - // } } } \ No newline at end of file diff --git a/Kyoo/Views/API/ShowsApi.cs b/Kyoo/Views/API/ShowsApi.cs index bda73f63..9f987e66 100644 --- a/Kyoo/Views/API/ShowsApi.cs +++ b/Kyoo/Views/API/ShowsApi.cs @@ -33,7 +33,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 20) { - where.Remove("showID"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -66,7 +65,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 20) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -99,7 +97,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 50) { - where.Remove("showID"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -132,7 +129,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 50) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -164,7 +160,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("showID"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -196,7 +191,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -229,7 +223,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("showID"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -262,7 +255,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -323,7 +315,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("showID"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -356,7 +347,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -389,7 +379,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("showID"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID"); @@ -422,7 +411,6 @@ namespace Kyoo.Api [FromQuery] Dictionary where, [FromQuery] int limit = 30) { - where.Remove("slug"); where.Remove("sortBy"); where.Remove("limit"); where.Remove("afterID");