diff --git a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs index d4be5192..18ea31ea 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs @@ -95,6 +95,7 @@ public class WatchStatusRepository : IWatchStatusRepository MovieId = movieId, Status = status, WatchedTime = watchedTime, + AddedDate = DateTime.UtcNow }; await _database.MovieWatchStatus.Upsert(ret) .UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed)) @@ -149,6 +150,7 @@ public class WatchStatusRepository : IWatchStatusRepository ) : null, UnseenEpisodesCount = unseenEpisodeCount, + AddedDate = DateTime.UtcNow }; await _database.ShowWatchStatus.Upsert(ret) .UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed)) @@ -211,6 +213,7 @@ public class WatchStatusRepository : IWatchStatusRepository Status = status, WatchedTime = watchedTime, WatchedPercent = percent, + AddedDate = DateTime.UtcNow }; await _database.EpisodeWatchStatus.Upsert(ret) .UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed)) diff --git a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs index 47c84b70..a91db99e 100644 --- a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs @@ -22,6 +22,7 @@ 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; @@ -108,5 +109,86 @@ namespace Kyoo.Core.Api ? NotFound() : NoContent(); } + + /// + /// Get watch status + /// + /// + /// Get when an item has been wathed and if it was watched. + /// + /// The ID or slug of the . + /// The status. + /// This episode does not have a specific status. + /// No episode with the given ID or slug could be found. + [HttpGet("{identifier:id}/watchStatus")] + [HttpGet("{identifier:id}/watchStatus", Order = AlternativeRoute)] + [UserOnly] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetWatchStatus(Identifier identifier) + { + return await _libraryManager.WatchStatus.GetEpisodeStatus( + identifier.IsSame(), + User.GetId()!.Value + ); + } + + /// + /// 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. + /// 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). + /// No episode with the given ID or slug could be found. + [HttpPost("{identifier:id}/watchStatus")] + [HttpPost("{identifier:id}/watchStatus", Order = AlternativeRoute)] + [UserOnly] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task SetWatchStatus(Identifier identifier, WatchStatus status, int? watchedTime) + { + int id = await identifier.Match( + id => Task.FromResult(id), + async slug => (await _libraryManager.Episodes.Get(slug)).Id + ); + return await _libraryManager.WatchStatus.SetEpisodeStatus( + id, + User.GetId()!.Value, + status, + watchedTime + ); + } + + /// + /// 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 episode with the given ID or slug could be found. + [HttpDelete("{identifier:id}/watchStatus")] + [HttpDelete("{identifier:id}/watchStatus", Order = AlternativeRoute)] + [UserOnly] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task DeleteWatchStatus(Identifier identifier) + { + await _libraryManager.WatchStatus.DeleteEpisodeStatus( + identifier.IsSame(), + User.GetId()!.Value + ); + } } } diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs index 077b99dd..98f11a78 100644 --- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs @@ -24,6 +24,7 @@ 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; @@ -226,5 +227,84 @@ namespace Kyoo.Core.Api 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 show does not have a specific status. + /// No show with the given ID or slug could be found. + [HttpGet("{identifier:id}/watchStatus")] + [HttpGet("{identifier:id}/watchStatus", Order = AlternativeRoute)] + [UserOnly] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetWatchStatus(Identifier identifier) + { + return await _libraryManager.WatchStatus.GetShowStatus( + identifier.IsSame(), + User.GetId()!.Value + ); + } + + /// + /// 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. + /// 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). + /// No movie with the given ID or slug could be found. + [HttpPost("{identifier:id}/watchStatus")] + [HttpPost("{identifier:id}/watchStatus", Order = AlternativeRoute)] + [UserOnly] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task SetWatchStatus(Identifier identifier, WatchStatus status) + { + int id = await identifier.Match( + id => Task.FromResult(id), + async slug => (await _libraryManager.Shows.Get(slug)).Id + ); + return await _libraryManager.WatchStatus.SetShowStatus( + id, + User.GetId()!.Value, + status + ); + } + + /// + /// 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 show with the given ID or slug could be found. + [HttpDelete("{identifier:id}/watchStatus")] + [HttpDelete("{identifier:id}/watchStatus", Order = AlternativeRoute)] + [UserOnly] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task DeleteWatchStatus(Identifier identifier) + { + await _libraryManager.WatchStatus.DeleteShowStatus( + identifier.IsSame(), + User.GetId()!.Value + ); + } } } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 333ca536..9ee2c7d0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -4,7 +4,7 @@ services: back: image: zoriya/kyoo_back:edge restart: unless-stopped - cpus: 0.8 + cpus: 1.5 env_file: - ./.env depends_on: @@ -36,7 +36,6 @@ services: transcoder: image: zoriya/kyoo_transcoder:edge restart: unless-stopped - cpus: 0.8 env_file: - ./.env volumes: