// Kyoo - A portable and vast media library solution. // Copyright (c) Kyoo. // // See AUTHORS.md and LICENSE file in the project root for full license information. // // Kyoo is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // Kyoo is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Utils; using Kyoo.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using static Kyoo.Abstractions.Models.Utils.Constants; namespace Kyoo.Core.Api { /// /// Information about one or multiple . /// [Route("movies")] [Route("movie", Order = AlternativeRoute)] [ApiController] [PartialPermission(nameof(Show))] [ApiDefinition("Shows", Group = ResourcesGroup)] public class MovieApi(ILibraryManager libraryManager, IThumbnailsManager thumbs) : TranscoderApi(libraryManager.Movies, thumbs) { /// /// Get studio that made the show /// /// /// Get the studio that made the show. /// /// The ID or slug of the . /// The aditional fields to include in the result. /// The studio that made the show. /// No show with the given ID or slug could be found. [HttpGet("{identifier:id}/studio")] [PartialPermission(Kind.Read)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetStudio( Identifier identifier, [FromQuery] Include fields ) { return await libraryManager.Studios.Get( identifier.IsContainedIn(x => x.Movies!), fields ); } /// /// Get collections containing this show /// /// /// List the collections that contain this show. /// /// The ID or slug of the . /// A key to sort collections by. /// An optional list of filters. /// The number of collections to return. /// The aditional fields to include in the result. /// A page of collections. /// The filters or the sort parameters are invalid. /// No show with the given ID or slug could be found. [HttpGet("{identifier:id}/collections")] [HttpGet("{identifier:id}/collection", Order = AlternativeRoute)] [PartialPermission(Kind.Read)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetCollections( Identifier identifier, [FromQuery] Sort sortBy, [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include fields ) { ICollection resources = await libraryManager.Collections.GetAll( Filter.And(filter, identifier.IsContainedIn(x => x.Movies)), sortBy, fields, pagination ); if ( !resources.Any() && await libraryManager.Movies.GetOrDefault(identifier.IsSame()) == null ) return NotFound(); return Page(resources, pagination.Limit); } /// /// Get watch status /// /// /// Get when an item has been wathed and if it was watched. /// /// The ID or slug of the . /// The status. /// This movie does not have a specific status. /// No movie with the given ID or slug could be found. [HttpGet("{identifier:id}/watchStatus")] [UserOnly] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetWatchStatus(Identifier identifier) { Guid id = await identifier.Match( id => Task.FromResult(id), async slug => (await libraryManager.Movies.Get(slug)).Id ); return await libraryManager.WatchStatus.GetMovieStatus(id, User.GetIdOrThrow()); } /// /// Set watch status /// /// /// Set when an item has been wathed and if it was watched. /// /// The ID or slug of the . /// The new watch status. /// Where the user stopped watching. /// Where the user stopped watching (in percent). /// The newly set status. /// The status has been set /// The status was not considered impactfull enough to be saved (less then 5% of watched for example). /// WatchedTime can't be specified if status is not watching. /// No movie with the given ID or slug could be found. [HttpPost("{identifier:id}/watchStatus")] [UserOnly] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task SetWatchStatus( Identifier identifier, WatchStatus status, int? watchedTime, int? percent ) { Guid id = await identifier.Match( id => Task.FromResult(id), async slug => (await libraryManager.Movies.Get(slug)).Id ); return await libraryManager.WatchStatus.SetMovieStatus( id, User.GetIdOrThrow(), status, watchedTime, percent ); } /// /// Delete watch status /// /// /// Delete watch status (to rewatch for example). /// /// The ID or slug of the . /// The newly set status. /// The status has been deleted. /// No movie with the given ID or slug could be found. [HttpDelete("{identifier:id}/watchStatus")] [UserOnly] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteWatchStatus(Identifier identifier) { Guid id = await identifier.Match( id => Task.FromResult(id), async slug => (await libraryManager.Movies.Get(slug)).Id ); await libraryManager.WatchStatus.DeleteMovieStatus(id, User.GetIdOrThrow()); } protected override async Task<(string path, string route)> GetPath(Identifier identifier) { string path = await identifier.Match( async id => (await Repository.Get(id)).Path, async slug => (await Repository.Get(slug)).Path ); return (path, $"/movies/{identifier}"); } } }