mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
API: documenting the season api
This commit is contained in:
parent
bd4fd848e1
commit
8a5e7fea06
@ -34,6 +34,6 @@ namespace Kyoo.Abstractions.Models.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for every <see cref="IResource"/>.
|
/// A group name for <see cref="ApiDefinitionAttribute"/>. It should be used for every <see cref="IResource"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ResourceGroup = "Resource";
|
public const string ResourceGroup = "Resources";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,23 @@ namespace Kyoo.Abstractions.Models.Utils
|
|||||||
return Expression.Lambda<Func<T, bool>>(equal);
|
return Expression.Lambda<Func<T, bool>>(equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A matcher overload for nullable IDs. See
|
||||||
|
/// <see cref="Matcher{T}(System.Linq.Expressions.Expression{System.Func{T,int}},System.Linq.Expressions.Expression{System.Func{T,string}})"/>
|
||||||
|
/// for more details.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idGetter">An expression to retrieve an ID from the type <typeparamref name="T"/>.</param>
|
||||||
|
/// <param name="slugGetter">An expression to retrieve a slug from the type <typeparamref name="T"/>.</param>
|
||||||
|
/// <typeparam name="T">The type to match against this identifier.</typeparam>
|
||||||
|
/// <returns>An expression to match the type <typeparamref name="T"/> to this identifier.</returns>
|
||||||
|
public Expression<Func<T, bool>> Matcher<T>(Expression<Func<T, int?>> idGetter,
|
||||||
|
Expression<Func<T, string>> slugGetter)
|
||||||
|
{
|
||||||
|
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||||
|
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||||
|
return Expression.Lambda<Func<T, bool>>(equal);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return true if this <see cref="Identifier"/> match a resource.
|
/// Return true if this <see cref="Identifier"/> match a resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -22,163 +22,114 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Abstractions.Models.Permissions;
|
using Kyoo.Abstractions.Models.Permissions;
|
||||||
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
[Route("api/season")]
|
/// <summary>
|
||||||
|
/// Information about one or multiple <see cref="Season"/>.
|
||||||
|
/// </summary>
|
||||||
[Route("api/seasons")]
|
[Route("api/seasons")]
|
||||||
|
[Route("api/season", Order = AlternativeRoute)]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[PartialPermission(nameof(SeasonApi))]
|
[PartialPermission(nameof(SeasonApi))]
|
||||||
public class SeasonApi : CrudApi<Season>
|
[ApiDefinition("Seasons", Group = ResourceGroup)]
|
||||||
|
public class SeasonApi : CrudThumbsApi<Season>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The library manager used to modify or retrieve information in the data store.
|
||||||
|
/// </summary>
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IThumbnailsManager _thumbs;
|
|
||||||
private readonly IFileSystem _files;
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="SeasonApi"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryManager">
|
||||||
|
/// The library manager used to modify or retrieve information in the data store.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="files">The file manager used to send images.</param>
|
||||||
|
/// <param name="thumbs">The thumbnail manager used to retrieve images paths.</param>
|
||||||
|
/// <param name="options">
|
||||||
|
/// Options used to retrieve the base URL of Kyoo.
|
||||||
|
/// </param>
|
||||||
public SeasonApi(ILibraryManager libraryManager,
|
public SeasonApi(ILibraryManager libraryManager,
|
||||||
IOptions<BasicOptions> options,
|
IFileSystem files,
|
||||||
IThumbnailsManager thumbs,
|
IThumbnailsManager thumbs,
|
||||||
IFileSystem files)
|
IOptions<BasicOptions> options)
|
||||||
: base(libraryManager.SeasonRepository, options.Value.PublicUrl)
|
: base(libraryManager.SeasonRepository, files, thumbs, options.Value.PublicUrl)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_thumbs = thumbs;
|
|
||||||
_files = files;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{seasonID:int}/episode")]
|
/// <summary>
|
||||||
[HttpGet("{seasonID:int}/episodes")]
|
/// Get episodes in the season
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// List the episodes that are part of the specified season.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="identifier">The ID or slug of the <see cref="Season"/>.</param>
|
||||||
|
/// <param name="sortBy">A key to sort episodes by.</param>
|
||||||
|
/// <param name="where">An optional list of filters.</param>
|
||||||
|
/// <param name="limit">The number of episodes to return.</param>
|
||||||
|
/// <param name="afterID">An optional episode's ID to start the query from this specific item.</param>
|
||||||
|
/// <returns>A page of episodes.</returns>
|
||||||
|
/// <response code="400">The filters or the sort parameters are invalid.</response>
|
||||||
|
/// <response code="404">No season with the given ID or slug could be found.</response>
|
||||||
|
[HttpGet("{identifier:id}/episodes")]
|
||||||
|
[HttpGet("{identifier:id}/episode", Order = AlternativeRoute)]
|
||||||
[PartialPermission(Kind.Read)]
|
[PartialPermission(Kind.Read)]
|
||||||
public async Task<ActionResult<Page<Episode>>> GetEpisode(int seasonID,
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<Page<Episode>>> GetEpisode(Identifier identifier,
|
||||||
[FromQuery] string sortBy,
|
[FromQuery] string sortBy,
|
||||||
[FromQuery] int afterID,
|
|
||||||
[FromQuery] Dictionary<string, string> where,
|
[FromQuery] Dictionary<string, string> where,
|
||||||
[FromQuery] int limit = 30)
|
[FromQuery] int limit = 30,
|
||||||
|
[FromQuery] int? afterID = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
ICollection<Episode> resources = await _libraryManager.GetAll(
|
||||||
ApiHelper.ParseWhere<Episode>(where, x => x.SeasonID == seasonID),
|
ApiHelper.ParseWhere(where, identifier.Matcher<Episode>(x => x.SeasonID, x => x.Season.Slug)),
|
||||||
new Sort<Episode>(sortBy),
|
new Sort<Episode>(sortBy),
|
||||||
new Pagination(limit, afterID));
|
new Pagination(limit, afterID));
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.GetOrDefault<Season>(seasonID) == null)
|
if (!resources.Any() && await _libraryManager.GetOrDefault(identifier.IsSame<Season>()) == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
return Page(resources, limit);
|
return Page(resources, limit);
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
return BadRequest(new { Error = ex.Message });
|
return BadRequest(new RequestError(ex.Message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{showSlug}-s{seasonNumber:int}/episode")]
|
/// <summary>
|
||||||
[HttpGet("{showSlug}-s{seasonNumber:int}/episodes")]
|
/// Get season's show
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Get the show that this season is part of.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="identifier">The ID or slug of the <see cref="Season"/>.</param>
|
||||||
|
/// <returns>The show that contains this season.</returns>
|
||||||
|
/// <response code="404">No season with the given ID or slug could be found.</response>
|
||||||
|
[HttpGet("{identifier:id}/show")]
|
||||||
[PartialPermission(Kind.Read)]
|
[PartialPermission(Kind.Read)]
|
||||||
public async Task<ActionResult<Page<Episode>>> GetEpisode(string showSlug,
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
int seasonNumber,
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[FromQuery] string sortBy,
|
public async Task<ActionResult<Show>> GetShow(Identifier identifier)
|
||||||
[FromQuery] int afterID,
|
|
||||||
[FromQuery] Dictionary<string, string> where,
|
|
||||||
[FromQuery] int limit = 30)
|
|
||||||
{
|
{
|
||||||
try
|
Show ret = await _libraryManager.GetOrDefault(identifier.IsContainedIn<Show, Season>(x => x.Seasons));
|
||||||
{
|
|
||||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
|
||||||
ApiHelper.ParseWhere<Episode>(where, x => x.Show.Slug == showSlug
|
|
||||||
&& x.SeasonNumber == seasonNumber),
|
|
||||||
new Sort<Episode>(sortBy),
|
|
||||||
new Pagination(limit, afterID));
|
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.GetOrDefault(showSlug, seasonNumber) == null)
|
|
||||||
return NotFound();
|
|
||||||
return Page(resources, limit);
|
|
||||||
}
|
|
||||||
catch (ArgumentException ex)
|
|
||||||
{
|
|
||||||
return BadRequest(new { Error = ex.Message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{showID:int}-s{seasonNumber:int}/episode")]
|
|
||||||
[HttpGet("{showID:int}-s{seasonNumber:int}/episodes")]
|
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
public async Task<ActionResult<Page<Episode>>> GetEpisode(int showID,
|
|
||||||
int seasonNumber,
|
|
||||||
[FromQuery] string sortBy,
|
|
||||||
[FromQuery] int afterID,
|
|
||||||
[FromQuery] Dictionary<string, string> where,
|
|
||||||
[FromQuery] int limit = 30)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ICollection<Episode> resources = await _libraryManager.GetAll(
|
|
||||||
ApiHelper.ParseWhere<Episode>(where, x => x.ShowID == showID && x.SeasonNumber == seasonNumber),
|
|
||||||
new Sort<Episode>(sortBy),
|
|
||||||
new Pagination(limit, afterID));
|
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.GetOrDefault(showID, seasonNumber) == null)
|
|
||||||
return NotFound();
|
|
||||||
return Page(resources, limit);
|
|
||||||
}
|
|
||||||
catch (ArgumentException ex)
|
|
||||||
{
|
|
||||||
return BadRequest(new { Error = ex.Message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{seasonID:int}/show")]
|
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
public async Task<ActionResult<Show>> GetShow(int seasonID)
|
|
||||||
{
|
|
||||||
Show ret = await _libraryManager.GetOrDefault<Show>(x => x.Seasons.Any(y => y.ID == seasonID));
|
|
||||||
if (ret == null)
|
if (ret == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{showSlug}-s{seasonNumber:int}/show")]
|
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
public async Task<ActionResult<Show>> GetShow(string showSlug, int seasonNumber)
|
|
||||||
{
|
|
||||||
Show ret = await _libraryManager.GetOrDefault<Show>(showSlug);
|
|
||||||
if (ret == null)
|
|
||||||
return NotFound();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{showID:int}-s{seasonNumber:int}/show")]
|
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
public async Task<ActionResult<Show>> GetShow(int showID, int seasonNumber)
|
|
||||||
{
|
|
||||||
Show ret = await _libraryManager.GetOrDefault<Show>(showID);
|
|
||||||
if (ret == null)
|
|
||||||
return NotFound();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}/poster")]
|
|
||||||
public async Task<IActionResult> GetPoster(int id)
|
|
||||||
{
|
|
||||||
Season season = await _libraryManager.GetOrDefault<Season>(id);
|
|
||||||
if (season == null)
|
|
||||||
return NotFound();
|
|
||||||
await _libraryManager.Load(season, x => x.Show);
|
|
||||||
return _files.FileResult(await _thumbs.GetImagePath(season, Images.Poster));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{slug}/poster")]
|
|
||||||
public async Task<IActionResult> GetPoster(string slug)
|
|
||||||
{
|
|
||||||
Season season = await _libraryManager.GetOrDefault<Season>(slug);
|
|
||||||
if (season == null)
|
|
||||||
return NotFound();
|
|
||||||
await _libraryManager.Load(season, x => x.Show);
|
|
||||||
return _files.FileResult(await _thumbs.GetImagePath(season, Images.Poster));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ namespace Kyoo.Core.Api
|
|||||||
public class ShowApi : CrudThumbsApi<Show>
|
public class ShowApi : CrudThumbsApi<Show>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The library manager used to modify or retrieve information about the data store.
|
/// The library manager used to modify or retrieve information in the data store.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ namespace Kyoo.Core.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get libraries containing this show.
|
/// Get libraries containing this show
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// List the libraries that contain this show. If this show is contained in a collection that is contained in
|
/// List the libraries that contain this show. If this show is contained in a collection that is contained in
|
||||||
@ -324,7 +324,7 @@ namespace Kyoo.Core.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get collections containing this show.
|
/// Get collections containing this show
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// List the collections that contain this show.
|
/// List the collections that contain this show.
|
||||||
@ -337,8 +337,8 @@ namespace Kyoo.Core.Api
|
|||||||
/// <returns>A page of collections.</returns>
|
/// <returns>A page of collections.</returns>
|
||||||
/// <response code="400">The filters or the sort parameters are invalid.</response>
|
/// <response code="400">The filters or the sort parameters are invalid.</response>
|
||||||
/// <response code="404">No show with the given ID or slug could be found.</response>
|
/// <response code="404">No show with the given ID or slug could be found.</response>
|
||||||
[HttpGet("{identifier:id}/collection")]
|
|
||||||
[HttpGet("{identifier:id}/collections")]
|
[HttpGet("{identifier:id}/collections")]
|
||||||
|
[HttpGet("{identifier:id}/collection", Order = AlternativeRoute)]
|
||||||
[PartialPermission(Kind.Read)]
|
[PartialPermission(Kind.Read)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user