diff --git a/Kyoo.Common/Controllers/ILibraryManager.cs b/Kyoo.Common/Controllers/ILibraryManager.cs index 33d65c42..a83721c0 100644 --- a/Kyoo.Common/Controllers/ILibraryManager.cs +++ b/Kyoo.Common/Controllers/ILibraryManager.cs @@ -55,11 +55,61 @@ namespace Kyoo.Controllers Pagination limit = default ) => GetSeasons(showSlug, where, new Sort(sort), limit); - Task> GetEpisodes(int showID, int seasonNumber); - Task> GetEpisodes(string showSlug, int seasonNumber); - Task> GetEpisodes(int seasonID); + Task> GetEpisodes(int showID, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodes(int showID, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodes(showID, where, new Sort(sort), limit); + Task> GetEpisodes(string showSlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodes(string showSlug, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodes(showSlug, where, new Sort(sort), limit); + Task> GetEpisodesFromSeason(int seasonID, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodesFromSeason(int seasonID, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodesFromSeason(seasonID, where, new Sort(sort), limit); + + Task> GetEpisodesFromSeason(int showID, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodesFromSeason(int showID, + int seasonNumber, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodesFromSeason(showID, seasonNumber, where, new Sort(sort), limit); + + Task> GetEpisodesFromSeason(string showSlug, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodesFromSeason(string showSlug, + int seasonNumber, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodesFromSeason(showSlug, seasonNumber, where, new Sort(sort), limit); + + // Helpers Task AddShowLink(int showID, int? libraryID, int? collectionID); Task AddShowLink([NotNull] Show show, Library library, Collection collection); diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index 2c971a13..7588d652 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -106,6 +106,7 @@ namespace Kyoo.Controllers public interface ISeasonRepository : IRepository { + Task Get(int showID, int seasonNumber); Task Get(string showSlug, int seasonNumber); Task Delete(string showSlug, int seasonNumber); @@ -128,7 +129,6 @@ namespace Kyoo.Controllers Expression> sort, Pagination limit = default ) => GetSeasons(showSlug, where, new Sort(sort), limit); - } public interface IEpisodeRepository : IRepository @@ -136,9 +136,57 @@ namespace Kyoo.Controllers Task Get(string showSlug, int seasonNumber, int episodeNumber); Task Delete(string showSlug, int seasonNumber, int episodeNumber); - Task> GetEpisodes(int showID, int seasonNumber); - Task> GetEpisodes(string showSlug, int seasonNumber); - Task> GetEpisodes(int seasonID); + Task> GetEpisodes(int showID, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodes(int showID, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodes(showID, where, new Sort(sort), limit); + + Task> GetEpisodes(string showSlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodes(string showSlug, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodes(showSlug, where, new Sort(sort), limit); + + Task> GetEpisodesFromSeason(int seasonID, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodesFromSeason(int seasonID, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodesFromSeason(seasonID, where, new Sort(sort), limit); + Task> GetEpisodesFromSeason(int showID, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodesFromSeason(int showID, + int seasonNumber, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodesFromSeason(showID, seasonNumber, where, new Sort(sort), limit); + Task> GetEpisodesFromSeason(string showSlug, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default); + Task> GetEpisodesFromSeason(string showSlug, + int seasonNumber, + [Optional] Expression> where, + Expression> sort, + Pagination limit = default + ) => GetEpisodesFromSeason(showSlug, seasonNumber, where, new Sort(sort), limit); } public interface ITrackRepository : IRepository diff --git a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs index 6956100c..52f3a600 100644 --- a/Kyoo.Common/Controllers/Implementations/LibraryManager.cs +++ b/Kyoo.Common/Controllers/Implementations/LibraryManager.cs @@ -213,21 +213,48 @@ namespace Kyoo.Controllers return SeasonRepository.GetSeasons(showSlug, where, sort, limit); } - public Task> GetEpisodes(int showID, int seasonNumber) + public Task> GetEpisodes(int showID, + Expression> where = null, + Sort sort = default, + Pagination limit = default) { - return EpisodeRepository.GetEpisodes(showID, seasonNumber); - } - - public Task> GetEpisodes(string showSlug, int seasonNumber) - { - return EpisodeRepository.GetEpisodes(showSlug, seasonNumber); - } - - public Task> GetEpisodes(int seasonID) - { - return EpisodeRepository.GetEpisodes(seasonID); + return EpisodeRepository.GetEpisodes(showID, where, sort, limit); } + public Task> GetEpisodes(string showSlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return EpisodeRepository.GetEpisodes(showSlug, where, sort, limit); + } + + public Task> GetEpisodesFromSeason(int seasonID, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return EpisodeRepository.GetEpisodesFromSeason(seasonID, where, sort, limit); + } + + public Task> GetEpisodesFromSeason(int showID, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return EpisodeRepository.GetEpisodesFromSeason(showID, seasonNumber, where, sort, limit); + } + + public Task> GetEpisodesFromSeason(string showSlug, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + return EpisodeRepository.GetEpisodesFromSeason(showSlug, seasonNumber, where, sort, limit); + } + public Task AddShowLink(int showID, int? libraryID, int? collectionID) { return ShowRepository.AddShowLink(showID, libraryID, collectionID); diff --git a/Kyoo.Common/Models/Exceptions/ItemNotFound.cs b/Kyoo.Common/Models/Exceptions/ItemNotFound.cs index c72427c6..f23cf363 100644 --- a/Kyoo.Common/Models/Exceptions/ItemNotFound.cs +++ b/Kyoo.Common/Models/Exceptions/ItemNotFound.cs @@ -6,6 +6,8 @@ namespace Kyoo.Models.Exceptions { public override string Message { get; } + public ItemNotFound() {} + public ItemNotFound(string message) { Message = message; diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index 8c83be3a..cbb8cd43 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -14,13 +14,21 @@ namespace Kyoo.Controllers { private readonly DatabaseContext _database; private readonly IProviderRepository _providers; + private readonly IShowRepository _shows; + private readonly ISeasonRepository _seasons; protected override Expression> DefaultSort => x => x.EpisodeNumber; - public EpisodeRepository(DatabaseContext database, IProviderRepository providers) : base(database) + public EpisodeRepository(DatabaseContext database, + IProviderRepository providers, + IShowRepository shows, + ISeasonRepository seasons) + : base(database) { _database = database; _providers = providers; + _shows = shows; + _seasons = seasons; } @@ -105,23 +113,80 @@ namespace Kyoo.Controllers } } - public async Task> GetEpisodes(int showID, int seasonNumber) + public async Task> GetEpisodes(int showID, + Expression> where = null, + Sort sort = default, + Pagination limit = default) { - return await _database.Episodes.Where(x => x.ShowID == showID - && x.SeasonNumber == seasonNumber).ToListAsync(); - } - - public async Task> GetEpisodes(string showSlug, int seasonNumber) - { - return await _database.Episodes.Where(x => x.Show.Slug == showSlug - && x.SeasonNumber == seasonNumber).ToListAsync(); - } - - public async Task> GetEpisodes(int seasonID) - { - return await _database.Episodes.Where(x => x.SeasonID == seasonID).ToListAsync(); + ICollection episodes = await ApplyFilters(_database.Episodes.Where(x => x.ShowID == showID), + where, + sort, + limit); + if (!episodes.Any() && await _shows.Get(showID) == null) + throw new ItemNotFound(); + return episodes; } + public async Task> GetEpisodes(string showSlug, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + ICollection episodes = await ApplyFilters(_database.Episodes.Where(x => x.Show.Slug == showSlug), + where, + sort, + limit); + if (!episodes.Any() && await _shows.Get(showSlug) == null) + throw new ItemNotFound(); + return episodes; + } + + public async Task> GetEpisodesFromSeason(int seasonID, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + ICollection episodes = await ApplyFilters(_database.Episodes.Where(x => x.SeasonID == seasonID), + where, + sort, + limit); + if (!episodes.Any() && await _seasons.Get(seasonID) == null) + throw new ItemNotFound(); + return episodes; + } + + public async Task> GetEpisodesFromSeason(int showID, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + ICollection episodes = await ApplyFilters(_database.Episodes.Where(x => x.ShowID == showID + && x.SeasonNumber == seasonNumber), + where, + sort, + limit); + if (!episodes.Any() && await _seasons.Get(showID, seasonNumber) == null) + throw new ItemNotFound(); + return episodes; + } + + public async Task> GetEpisodesFromSeason(string showSlug, + int seasonNumber, + Expression> where = null, + Sort sort = default, + Pagination limit = default) + { + ICollection episodes = await ApplyFilters(_database.Episodes.Where(x => x.Show.Slug == showSlug + && x.SeasonNumber == seasonNumber), + where, + sort, + limit); + if (!episodes.Any() && await _seasons.Get(showSlug, seasonNumber) == null) + throw new ItemNotFound(); + return episodes; + } + public async Task Delete(string showSlug, int seasonNumber, int episodeNumber) { Episode obj = await Get(showSlug, seasonNumber, episodeNumber); diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs index 2d9fc938..cec35eeb 100644 --- a/Kyoo/Controllers/Repositories/SeasonRepository.cs +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -15,15 +15,20 @@ namespace Kyoo.Controllers private readonly DatabaseContext _database; private readonly IProviderRepository _providers; private readonly IEpisodeRepository _episodes; + private readonly IShowRepository _shows; protected override Expression> DefaultSort => x => x.SeasonNumber; - public SeasonRepository(DatabaseContext database, IProviderRepository providers, IEpisodeRepository episodes) + public SeasonRepository(DatabaseContext database, + IProviderRepository providers, + IEpisodeRepository episodes, + IShowRepository shows) : base(database) { _database = database; _providers = providers; _episodes = episodes; + _shows = shows; } @@ -50,6 +55,12 @@ namespace Kyoo.Controllers return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value)); } + public Task Get(int showID, int seasonNumber) + { + return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID + && x.SeasonNumber == seasonNumber); + } + public Task Get(string showSlug, int seasonNumber) { return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug @@ -102,26 +113,32 @@ namespace Kyoo.Controllers } } - public Task> GetSeasons(int showID, + public async Task> GetSeasons(int showID, Expression> where = null, Sort sort = default, Pagination limit = default) { - return ApplyFilters(_database.Seasons.Where(x => x.ShowID == showID), + ICollection seasons = await ApplyFilters(_database.Seasons.Where(x => x.ShowID == showID), where, sort, limit); + if (!seasons.Any() && await _shows.Get(showID) == null) + throw new ItemNotFound(); + return seasons; } - public Task> GetSeasons(string showSlug, + public async Task> GetSeasons(string showSlug, Expression> where = null, Sort sort = default, Pagination limit = default) { - return ApplyFilters(_database.Seasons.Where(x => x.Show.Slug == showSlug), + ICollection seasons = await ApplyFilters(_database.Seasons.Where(x => x.Show.Slug == showSlug), where, sort, limit); + if (!seasons.Any() && await _shows.Get(showSlug) == null) + throw new ItemNotFound(); + return seasons; } public async Task Delete(string showSlug, int seasonNumber) diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index b0335786..3e9d9710 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -171,6 +171,7 @@ namespace Kyoo.Controllers await _database.SaveChangesAsync(); + // TODO fix circular references of Show/Season/Episode Repository. if (obj.Seasons != null) await _seasons.DeleteRange(obj.Seasons); diff --git a/Kyoo/Views/API/EpisodesAPI.cs b/Kyoo/Views/API/EpisodesAPI.cs index abf0650d..736ffb02 100644 --- a/Kyoo/Views/API/EpisodesAPI.cs +++ b/Kyoo/Views/API/EpisodesAPI.cs @@ -1,4 +1,5 @@ -using Kyoo.Models; +using System; +using Kyoo.Models; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Linq; @@ -21,14 +22,9 @@ namespace Kyoo.Api [HttpGet("{showSlug}/season/{seasonNumber}")] [Authorize(Policy="Read")] - public async Task>> GetEpisodesForSeason(string showSlug, int seasonNumber) + public Task>> GetEpisodesForSeason(string showSlug, int seasonNumber) { - IEnumerable episodes = await _libraryManager.GetEpisodes(showSlug, seasonNumber); - - if(episodes == null) - return NotFound(); - - return episodes.ToList(); + throw new NotImplementedException(); } [HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")] diff --git a/Kyoo/Views/API/ShowsApi.cs b/Kyoo/Views/API/ShowsApi.cs index b74a398c..cf7d300b 100644 --- a/Kyoo/Views/API/ShowsApi.cs +++ b/Kyoo/Views/API/ShowsApi.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Kyoo.CommonApi; using Kyoo.Controllers; +using Kyoo.Models.Exceptions; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Configuration; @@ -47,6 +48,10 @@ namespace Kyoo.Api return Page(ressources, limit); } + catch (ItemNotFound) + { + return NotFound(); + } catch (ArgumentException ex) { return BadRequest(new {Error = ex.Message}); @@ -76,6 +81,76 @@ namespace Kyoo.Api return Page(ressources, limit); } + catch (ItemNotFound) + { + return NotFound(); + } + catch (ArgumentException ex) + { + return BadRequest(new {Error = ex.Message}); + } + } + + [HttpGet("{showID:int}/episode")] + [HttpGet("{showID:int}/episodes")] + [Authorize(Policy = "Read")] + public async Task>> GetEpisodes(int showID, + [FromQuery] string sortBy, + [FromQuery] int afterID, + [FromQuery] Dictionary where, + [FromQuery] int limit = 50) + { + where.Remove("showID"); + where.Remove("sortBy"); + where.Remove("limit"); + where.Remove("afterID"); + + try + { + ICollection ressources = await _libraryManager.GetEpisodes(showID, + 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}/episode")] + [HttpGet("{slug}/episodes")] + [Authorize(Policy = "Read")] + public async Task>> GetEpisodes(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.GetEpisodes(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});