Adding episodes related info & NotFound exceptions

This commit is contained in:
Zoe Roux 2020-07-24 02:47:31 +02:00
parent 00ed7a4863
commit 59e72b6dca
9 changed files with 328 additions and 47 deletions

View File

@ -55,11 +55,61 @@ namespace Kyoo.Controllers
Pagination limit = default Pagination limit = default
) => GetSeasons(showSlug, where, new Sort<Season>(sort), limit); ) => GetSeasons(showSlug, where, new Sort<Season>(sort), limit);
Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber); Task<ICollection<Episode>> GetEpisodes(int showID,
Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber); Expression<Func<Episode, bool>> where = null,
Task<ICollection<Episode>> GetEpisodes(int seasonID); Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodes(int showID,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodes(showID, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodes(string showSlug,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodes(string showSlug,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodes(showSlug, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodesFromSeason(int seasonID,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodesFromSeason(int seasonID,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodesFromSeason(seasonID, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodesFromSeason(int showID,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodesFromSeason(int showID,
int seasonNumber,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodesFromSeason(showID, seasonNumber, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodesFromSeason(string showSlug,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodesFromSeason(string showSlug,
int seasonNumber,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodesFromSeason(showSlug, seasonNumber, where, new Sort<Episode>(sort), limit);
// Helpers // Helpers
Task AddShowLink(int showID, int? libraryID, int? collectionID); Task AddShowLink(int showID, int? libraryID, int? collectionID);
Task AddShowLink([NotNull] Show show, Library library, Collection collection); Task AddShowLink([NotNull] Show show, Library library, Collection collection);

View File

@ -106,6 +106,7 @@ namespace Kyoo.Controllers
public interface ISeasonRepository : IRepository<Season> public interface ISeasonRepository : IRepository<Season>
{ {
Task<Season> Get(int showID, int seasonNumber);
Task<Season> Get(string showSlug, int seasonNumber); Task<Season> Get(string showSlug, int seasonNumber);
Task Delete(string showSlug, int seasonNumber); Task Delete(string showSlug, int seasonNumber);
@ -128,7 +129,6 @@ namespace Kyoo.Controllers
Expression<Func<Season, object>> sort, Expression<Func<Season, object>> sort,
Pagination limit = default Pagination limit = default
) => GetSeasons(showSlug, where, new Sort<Season>(sort), limit); ) => GetSeasons(showSlug, where, new Sort<Season>(sort), limit);
} }
public interface IEpisodeRepository : IRepository<Episode> public interface IEpisodeRepository : IRepository<Episode>
@ -136,9 +136,57 @@ namespace Kyoo.Controllers
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber); Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
Task Delete(string showSlug, int seasonNumber, int episodeNumber); Task Delete(string showSlug, int seasonNumber, int episodeNumber);
Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber); Task<ICollection<Episode>> GetEpisodes(int showID,
Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber); Expression<Func<Episode, bool>> where = null,
Task<ICollection<Episode>> GetEpisodes(int seasonID); Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodes(int showID,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodes(showID, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodes(string showSlug,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodes(string showSlug,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodes(showSlug, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodesFromSeason(int seasonID,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodesFromSeason(int seasonID,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodesFromSeason(seasonID, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodesFromSeason(int showID,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodesFromSeason(int showID,
int seasonNumber,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodesFromSeason(showID, seasonNumber, where, new Sort<Episode>(sort), limit);
Task<ICollection<Episode>> GetEpisodesFromSeason(string showSlug,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default);
Task<ICollection<Episode>> GetEpisodesFromSeason(string showSlug,
int seasonNumber,
[Optional] Expression<Func<Episode, bool>> where,
Expression<Func<Episode, object>> sort,
Pagination limit = default
) => GetEpisodesFromSeason(showSlug, seasonNumber, where, new Sort<Episode>(sort), limit);
} }
public interface ITrackRepository : IRepository<Track> public interface ITrackRepository : IRepository<Track>

View File

@ -213,21 +213,48 @@ namespace Kyoo.Controllers
return SeasonRepository.GetSeasons(showSlug, where, sort, limit); return SeasonRepository.GetSeasons(showSlug, where, sort, limit);
} }
public Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber) public Task<ICollection<Episode>> GetEpisodes(int showID,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{ {
return EpisodeRepository.GetEpisodes(showID, seasonNumber); return EpisodeRepository.GetEpisodes(showID, where, sort, limit);
}
public Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber)
{
return EpisodeRepository.GetEpisodes(showSlug, seasonNumber);
}
public Task<ICollection<Episode>> GetEpisodes(int seasonID)
{
return EpisodeRepository.GetEpisodes(seasonID);
} }
public Task<ICollection<Episode>> GetEpisodes(string showSlug,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
return EpisodeRepository.GetEpisodes(showSlug, where, sort, limit);
}
public Task<ICollection<Episode>> GetEpisodesFromSeason(int seasonID,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
return EpisodeRepository.GetEpisodesFromSeason(seasonID, where, sort, limit);
}
public Task<ICollection<Episode>> GetEpisodesFromSeason(int showID,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
return EpisodeRepository.GetEpisodesFromSeason(showID, seasonNumber, where, sort, limit);
}
public Task<ICollection<Episode>> GetEpisodesFromSeason(string showSlug,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
return EpisodeRepository.GetEpisodesFromSeason(showSlug, seasonNumber, where, sort, limit);
}
public Task AddShowLink(int showID, int? libraryID, int? collectionID) public Task AddShowLink(int showID, int? libraryID, int? collectionID)
{ {
return ShowRepository.AddShowLink(showID, libraryID, collectionID); return ShowRepository.AddShowLink(showID, libraryID, collectionID);

View File

@ -6,6 +6,8 @@ namespace Kyoo.Models.Exceptions
{ {
public override string Message { get; } public override string Message { get; }
public ItemNotFound() {}
public ItemNotFound(string message) public ItemNotFound(string message)
{ {
Message = message; Message = message;

View File

@ -14,13 +14,21 @@ namespace Kyoo.Controllers
{ {
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
private readonly IProviderRepository _providers; private readonly IProviderRepository _providers;
private readonly IShowRepository _shows;
private readonly ISeasonRepository _seasons;
protected override Expression<Func<Episode, object>> DefaultSort => x => x.EpisodeNumber; protected override Expression<Func<Episode, object>> 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; _database = database;
_providers = providers; _providers = providers;
_shows = shows;
_seasons = seasons;
} }
@ -105,23 +113,80 @@ namespace Kyoo.Controllers
} }
} }
public async Task<ICollection<Episode>> GetEpisodes(int showID, int seasonNumber) public async Task<ICollection<Episode>> GetEpisodes(int showID,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{ {
return await _database.Episodes.Where(x => x.ShowID == showID ICollection<Episode> episodes = await ApplyFilters(_database.Episodes.Where(x => x.ShowID == showID),
&& x.SeasonNumber == seasonNumber).ToListAsync(); where,
} sort,
limit);
public async Task<ICollection<Episode>> GetEpisodes(string showSlug, int seasonNumber) if (!episodes.Any() && await _shows.Get(showID) == null)
{ throw new ItemNotFound();
return await _database.Episodes.Where(x => x.Show.Slug == showSlug return episodes;
&& x.SeasonNumber == seasonNumber).ToListAsync();
}
public async Task<ICollection<Episode>> GetEpisodes(int seasonID)
{
return await _database.Episodes.Where(x => x.SeasonID == seasonID).ToListAsync();
} }
public async Task<ICollection<Episode>> GetEpisodes(string showSlug,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
ICollection<Episode> 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<ICollection<Episode>> GetEpisodesFromSeason(int seasonID,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
ICollection<Episode> 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<ICollection<Episode>> GetEpisodesFromSeason(int showID,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
ICollection<Episode> 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<ICollection<Episode>> GetEpisodesFromSeason(string showSlug,
int seasonNumber,
Expression<Func<Episode, bool>> where = null,
Sort<Episode> sort = default,
Pagination limit = default)
{
ICollection<Episode> 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) public async Task Delete(string showSlug, int seasonNumber, int episodeNumber)
{ {
Episode obj = await Get(showSlug, seasonNumber, episodeNumber); Episode obj = await Get(showSlug, seasonNumber, episodeNumber);

View File

@ -15,15 +15,20 @@ namespace Kyoo.Controllers
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
private readonly IProviderRepository _providers; private readonly IProviderRepository _providers;
private readonly IEpisodeRepository _episodes; private readonly IEpisodeRepository _episodes;
private readonly IShowRepository _shows;
protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber; protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber;
public SeasonRepository(DatabaseContext database, IProviderRepository providers, IEpisodeRepository episodes) public SeasonRepository(DatabaseContext database,
IProviderRepository providers,
IEpisodeRepository episodes,
IShowRepository shows)
: base(database) : base(database)
{ {
_database = database; _database = database;
_providers = providers; _providers = providers;
_episodes = episodes; _episodes = episodes;
_shows = shows;
} }
@ -50,6 +55,12 @@ namespace Kyoo.Controllers
return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value)); return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value));
} }
public Task<Season> Get(int showID, int seasonNumber)
{
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
&& x.SeasonNumber == seasonNumber);
}
public Task<Season> Get(string showSlug, int seasonNumber) public Task<Season> Get(string showSlug, int seasonNumber)
{ {
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
@ -102,26 +113,32 @@ namespace Kyoo.Controllers
} }
} }
public Task<ICollection<Season>> GetSeasons(int showID, public async Task<ICollection<Season>> GetSeasons(int showID,
Expression<Func<Season, bool>> where = null, Expression<Func<Season, bool>> where = null,
Sort<Season> sort = default, Sort<Season> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return ApplyFilters(_database.Seasons.Where(x => x.ShowID == showID), ICollection<Season> seasons = await ApplyFilters(_database.Seasons.Where(x => x.ShowID == showID),
where, where,
sort, sort,
limit); limit);
if (!seasons.Any() && await _shows.Get(showID) == null)
throw new ItemNotFound();
return seasons;
} }
public Task<ICollection<Season>> GetSeasons(string showSlug, public async Task<ICollection<Season>> GetSeasons(string showSlug,
Expression<Func<Season, bool>> where = null, Expression<Func<Season, bool>> where = null,
Sort<Season> sort = default, Sort<Season> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return ApplyFilters(_database.Seasons.Where(x => x.Show.Slug == showSlug), ICollection<Season> seasons = await ApplyFilters(_database.Seasons.Where(x => x.Show.Slug == showSlug),
where, where,
sort, sort,
limit); limit);
if (!seasons.Any() && await _shows.Get(showSlug) == null)
throw new ItemNotFound();
return seasons;
} }
public async Task Delete(string showSlug, int seasonNumber) public async Task Delete(string showSlug, int seasonNumber)

View File

@ -171,6 +171,7 @@ namespace Kyoo.Controllers
await _database.SaveChangesAsync(); await _database.SaveChangesAsync();
// TODO fix circular references of Show/Season/Episode Repository.
if (obj.Seasons != null) if (obj.Seasons != null)
await _seasons.DeleteRange(obj.Seasons); await _seasons.DeleteRange(obj.Seasons);

View File

@ -1,4 +1,5 @@
using Kyoo.Models; using System;
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -21,14 +22,9 @@ namespace Kyoo.Api
[HttpGet("{showSlug}/season/{seasonNumber}")] [HttpGet("{showSlug}/season/{seasonNumber}")]
[Authorize(Policy="Read")] [Authorize(Policy="Read")]
public async Task<ActionResult<IEnumerable<Episode>>> GetEpisodesForSeason(string showSlug, int seasonNumber) public Task<ActionResult<IEnumerable<Episode>>> GetEpisodesForSeason(string showSlug, int seasonNumber)
{ {
IEnumerable<Episode> episodes = await _libraryManager.GetEpisodes(showSlug, seasonNumber); throw new NotImplementedException();
if(episodes == null)
return NotFound();
return episodes.ToList();
} }
[HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")] [HttpGet("{showSlug}/season/{seasonNumber}/episode/{episodeNumber}")]

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Kyoo.CommonApi; using Kyoo.CommonApi;
using Kyoo.Controllers; using Kyoo.Controllers;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -47,6 +48,10 @@ namespace Kyoo.Api
return Page(ressources, limit); return Page(ressources, limit);
} }
catch (ItemNotFound)
{
return NotFound();
}
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new {Error = ex.Message});
@ -76,6 +81,76 @@ namespace Kyoo.Api
return Page(ressources, limit); 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<ActionResult<Page<Episode>>> GetEpisodes(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 50)
{
where.Remove("showID");
where.Remove("sortBy");
where.Remove("limit");
where.Remove("afterID");
try
{
ICollection<Episode> ressources = await _libraryManager.GetEpisodes(showID,
ApiHelper.ParseWhere<Episode>(where),
new Sort<Episode>(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<ActionResult<Page<Episode>>> GetEpisodes(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
[FromQuery] int limit = 20)
{
where.Remove("slug");
where.Remove("sortBy");
where.Remove("limit");
where.Remove("afterID");
try
{
ICollection<Episode> ressources = await _libraryManager.GetEpisodes(slug,
ApiHelper.ParseWhere<Episode>(where),
new Sort<Episode>(sortBy),
new Pagination(limit, afterID));
return Page(ressources, limit);
}
catch (ItemNotFound)
{
return NotFound();
}
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
return BadRequest(new {Error = ex.Message}); return BadRequest(new {Error = ex.Message});