mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-22 15:00:35 -04:00
Add percent, unseen episodes count and show completions handling
This commit is contained in:
parent
32050bcdcd
commit
6567e78c8c
@ -52,7 +52,15 @@ public interface IWatchStatusRepository
|
|||||||
/// <param name="watchedTime">Where the user has stopped watching. Only usable if Status
|
/// <param name="watchedTime">Where the user has stopped watching. Only usable if Status
|
||||||
/// is <see cref="WatchStatus.Watching"/></param>
|
/// is <see cref="WatchStatus.Watching"/></param>
|
||||||
/// <returns>The movie's status</returns>
|
/// <returns>The movie's status</returns>
|
||||||
Task<MovieWatchStatus> SetMovieStatus(int movieId, int userId, WatchStatus status, int? watchedTime);
|
Task<MovieWatchStatus?> SetMovieStatus(int movieId, int userId, WatchStatus status, int? watchedTime);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete the watch status of a movie.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="where">The movie selector.</param>
|
||||||
|
/// <param name="userId">The id of the user.</param>
|
||||||
|
/// <returns>Nothing.</returns>
|
||||||
|
Task DeleteMovieStatus(Expression<Func<Movie, bool>> where, int userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the watch status of a show.
|
/// Get the watch status of a show.
|
||||||
@ -69,7 +77,15 @@ public interface IWatchStatusRepository
|
|||||||
/// <param name="userId">The id of the user.</param>
|
/// <param name="userId">The id of the user.</param>
|
||||||
/// <param name="status">The new status.</param>
|
/// <param name="status">The new status.</param>
|
||||||
/// <returns>The shows's status</returns>
|
/// <returns>The shows's status</returns>
|
||||||
Task<ShowWatchStatus> SetShowStatus(int showId, int userId, WatchStatus status);
|
Task<ShowWatchStatus?> SetShowStatus(int showId, int userId, WatchStatus status);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete the watch status of a show.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="where">The show selector.</param>
|
||||||
|
/// <param name="userId">The id of the user.</param>
|
||||||
|
/// <returns>Nothing.</returns>
|
||||||
|
Task DeleteShowStatus(Expression<Func<Show, bool>> where, int userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the watch status of an episode.
|
/// Get the watch status of an episode.
|
||||||
@ -88,5 +104,13 @@ public interface IWatchStatusRepository
|
|||||||
/// <param name="watchedTime">Where the user has stopped watching. Only usable if Status
|
/// <param name="watchedTime">Where the user has stopped watching. Only usable if Status
|
||||||
/// is <see cref="WatchStatus.Watching"/></param>
|
/// is <see cref="WatchStatus.Watching"/></param>
|
||||||
/// <returns>The episode's status</returns>
|
/// <returns>The episode's status</returns>
|
||||||
Task<EpisodeWatchStatus> SetEpisodeStatus(int episodeId, int userId, WatchStatus status, int? watchedTime);
|
Task<EpisodeWatchStatus?> SetEpisodeStatus(int episodeId, int userId, WatchStatus status, int? watchedTime);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete the watch status of an episode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="where">The episode selector.</param>
|
||||||
|
/// <param name="userId">The id of the user.</param>
|
||||||
|
/// <returns>Nothing.</returns>
|
||||||
|
Task DeleteEpisodeStatus(Expression<Func<Episode, bool>> where, int userId);
|
||||||
}
|
}
|
||||||
|
@ -178,6 +178,15 @@ namespace Kyoo.Abstractions.Models
|
|||||||
.ThenBy(x => x.EpisodeNumber)
|
.ThenBy(x => x.EpisodeNumber)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of episodes in this season.
|
||||||
|
/// </summary>
|
||||||
|
[Projectable(UseMemberBody = nameof(_EpisodesCount), OnlyOnInclude = true)]
|
||||||
|
[NotMapped]
|
||||||
|
public int EpisodesCount { get; set; }
|
||||||
|
|
||||||
|
private int _EpisodesCount => Episodes!.Count;
|
||||||
|
|
||||||
[SerializeIgnore] public ICollection<ShowWatchStatus> Watched { get; set; }
|
[SerializeIgnore] public ICollection<ShowWatchStatus> Watched { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using EntityFrameworkCore.Projectables;
|
using EntityFrameworkCore.Projectables;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
@ -89,6 +90,14 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// Null if the status is not Watching.
|
/// Null if the status is not Watching.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int? WatchedTime { get; set; }
|
public int? WatchedTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where the player has stopped watching the movie (in percentage between 0 and 100).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Null if the status is not Watching.
|
||||||
|
/// </remarks>
|
||||||
|
public int? WatchedPercent { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EpisodeWatchStatus : IAddedDate
|
public class EpisodeWatchStatus : IAddedDate
|
||||||
@ -128,6 +137,14 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// Null if the status is not Watching.
|
/// Null if the status is not Watching.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int? WatchedTime { get; set; }
|
public int? WatchedTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where the player has stopped watching the episode (in percentage between 0 and 100).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Null if the status is not Watching or if the next episode is not started.
|
||||||
|
/// </remarks>
|
||||||
|
public int? WatchedPercent { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShowWatchStatus : IAddedDate
|
public class ShowWatchStatus : IAddedDate
|
||||||
@ -160,6 +177,11 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public WatchStatus Status { get; set; }
|
public WatchStatus Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The numder of episodes the user has not seen.
|
||||||
|
/// </summary>
|
||||||
|
public int UnseenEpisodesCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID of the episode started.
|
/// The ID of the episode started.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -177,8 +199,21 @@ namespace Kyoo.Abstractions.Models
|
|||||||
/// Null if the status is not Watching or if the next episode is not started.
|
/// Null if the status is not Watching or if the next episode is not started.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Projectable(UseMemberBody = nameof(_WatchedTime), NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
|
[Projectable(UseMemberBody = nameof(_WatchedTime), NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
|
||||||
|
[NotMapped]
|
||||||
public int? WatchedTime { get; set; }
|
public int? WatchedTime { get; set; }
|
||||||
|
|
||||||
private int? _WatchedTime => NextEpisode?.Watched.FirstOrDefault()?.WatchedTime;
|
private int? _WatchedTime => NextEpisode?.Watched.FirstOrDefault()?.WatchedTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where the player has stopped watching the episode (in percentage between 0 and 100).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Null if the status is not Watching or if the next episode is not started.
|
||||||
|
/// </remarks>
|
||||||
|
[Projectable(UseMemberBody = nameof(_WatchedPercent), NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
|
||||||
|
[NotMapped]
|
||||||
|
public int? WatchedPercent { get; set; }
|
||||||
|
|
||||||
|
private int? _WatchedPercent => NextEpisode?.Watched.FirstOrDefault()?.WatchedPercent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,33 +30,65 @@ namespace Kyoo.Core.Controllers;
|
|||||||
|
|
||||||
public class WatchStatusRepository : IWatchStatusRepository
|
public class WatchStatusRepository : IWatchStatusRepository
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If the watch percent is below this value, don't consider the item started.
|
||||||
|
/// </summary>
|
||||||
|
public const int MinWatchPercent = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the watch percent is higher than this value, consider the item completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This value is lower to account credits in movies that can last really long.
|
||||||
|
/// </remarks>
|
||||||
|
public const int MaxWatchPercent = 90;
|
||||||
|
|
||||||
private readonly DatabaseContext _database;
|
private readonly DatabaseContext _database;
|
||||||
private readonly IRepository<Episode> _episodes;
|
private readonly IRepository<Episode> _episodes;
|
||||||
|
private readonly IRepository<Movie> _movies;
|
||||||
|
|
||||||
public WatchStatusRepository(DatabaseContext database, IRepository<Episode> episodes)
|
public WatchStatusRepository(DatabaseContext database,
|
||||||
|
IRepository<Episode> episodes,
|
||||||
|
IRepository<Movie> movies)
|
||||||
{
|
{
|
||||||
_database = database;
|
_database = database;
|
||||||
_episodes = episodes;
|
_episodes = episodes;
|
||||||
|
_movies = movies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<MovieWatchStatus?> GetMovieStatus(Expression<Func<Movie, bool>> where, int userId)
|
public Task<MovieWatchStatus?> GetMovieStatus(Expression<Func<Movie, bool>> where, int userId)
|
||||||
{
|
{
|
||||||
return _database.MovieWatchInfo.FirstOrDefaultAsync(x =>
|
return _database.MovieWatchStatus.FirstOrDefaultAsync(x =>
|
||||||
x.Movie == _database.Movies.FirstOrDefault(where)
|
x.Movie == _database.Movies.FirstOrDefault(where)
|
||||||
&& x.UserId == userId
|
&& x.UserId == userId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<MovieWatchStatus> SetMovieStatus(
|
public async Task<MovieWatchStatus?> SetMovieStatus(
|
||||||
int movieId,
|
int movieId,
|
||||||
int userId,
|
int userId,
|
||||||
WatchStatus status,
|
WatchStatus status,
|
||||||
int? watchedTime)
|
int? watchedTime)
|
||||||
{
|
{
|
||||||
|
Movie movie = await _movies.Get(movieId);
|
||||||
|
int? percent = watchedTime != null && movie.Runtime > 0
|
||||||
|
? (int)Math.Round(watchedTime.Value / (movie.Runtime * 60f) * 100f)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (percent < MinWatchPercent)
|
||||||
|
return null;
|
||||||
|
if (percent > MaxWatchPercent)
|
||||||
|
{
|
||||||
|
status = WatchStatus.Completed;
|
||||||
|
watchedTime = null;
|
||||||
|
percent = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (watchedTime.HasValue && status != WatchStatus.Watching)
|
if (watchedTime.HasValue && status != WatchStatus.Watching)
|
||||||
throw new ValidationException("Can't have a watched time if the status is not watching.");
|
throw new ValidationException("Can't have a watched time if the status is not watching.");
|
||||||
|
|
||||||
MovieWatchStatus ret = new()
|
MovieWatchStatus ret = new()
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
@ -64,27 +96,45 @@ public class WatchStatusRepository : IWatchStatusRepository
|
|||||||
Status = status,
|
Status = status,
|
||||||
WatchedTime = watchedTime,
|
WatchedTime = watchedTime,
|
||||||
};
|
};
|
||||||
await _database.MovieWatchInfo.Upsert(ret)
|
await _database.MovieWatchStatus.Upsert(ret)
|
||||||
.UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed))
|
.UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed))
|
||||||
.RunAsync();
|
.RunAsync();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task DeleteMovieStatus(
|
||||||
|
Expression<Func<Movie, bool>> where,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
await _database.MovieWatchStatus
|
||||||
|
.Where(x => x.Movie == _database.Movies.FirstOrDefault(where)
|
||||||
|
&& x.UserId == userId)
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<ShowWatchStatus?> GetShowStatus(Expression<Func<Show, bool>> where, int userId)
|
public Task<ShowWatchStatus?> GetShowStatus(Expression<Func<Show, bool>> where, int userId)
|
||||||
{
|
{
|
||||||
return _database.ShowWatchInfo.FirstOrDefaultAsync(x =>
|
return _database.ShowWatchStatus.FirstOrDefaultAsync(x =>
|
||||||
x.Show == _database.Shows.FirstOrDefault(where)
|
x.Show == _database.Shows.FirstOrDefault(where)
|
||||||
&& x.UserId == userId
|
&& x.UserId == userId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<ShowWatchStatus> SetShowStatus(
|
public async Task<ShowWatchStatus?> SetShowStatus(
|
||||||
int showId,
|
int showId,
|
||||||
int userId,
|
int userId,
|
||||||
WatchStatus status)
|
WatchStatus status)
|
||||||
{
|
{
|
||||||
|
int unseenEpisodeCount = await _database.Episodes
|
||||||
|
.Where(x => x.ShowId == showId)
|
||||||
|
.Where(x => x.WatchStatus!.Status != WatchStatus.Completed)
|
||||||
|
.CountAsync();
|
||||||
|
if (unseenEpisodeCount == 0)
|
||||||
|
status = WatchStatus.Completed;
|
||||||
|
|
||||||
ShowWatchStatus ret = new()
|
ShowWatchStatus ret = new()
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
@ -98,41 +148,85 @@ public class WatchStatusRepository : IWatchStatusRepository
|
|||||||
reverse: true
|
reverse: true
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
UnseenEpisodesCount = unseenEpisodeCount,
|
||||||
};
|
};
|
||||||
await _database.ShowWatchInfo.Upsert(ret)
|
await _database.ShowWatchStatus.Upsert(ret)
|
||||||
.UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed))
|
.UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed))
|
||||||
.RunAsync();
|
.RunAsync();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task DeleteShowStatus(
|
||||||
|
Expression<Func<Show, bool>> where,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
await _database.ShowWatchStatus
|
||||||
|
.Where(x => x.Show == _database.Shows.FirstOrDefault(where)
|
||||||
|
&& x.UserId == userId)
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
await _database.EpisodeWatchStatus
|
||||||
|
.Where(x => x.Episode.Show == _database.Shows.FirstOrDefault(where)
|
||||||
|
&& x.UserId == userId)
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<EpisodeWatchStatus?> GetEpisodeStatus(Expression<Func<Episode, bool>> where, int userId)
|
public Task<EpisodeWatchStatus?> GetEpisodeStatus(Expression<Func<Episode, bool>> where, int userId)
|
||||||
{
|
{
|
||||||
return _database.EpisodeWatchInfo.FirstOrDefaultAsync(x =>
|
return _database.EpisodeWatchStatus.FirstOrDefaultAsync(x =>
|
||||||
x.Episode == _database.Episodes.FirstOrDefault(where)
|
x.Episode == _database.Episodes.FirstOrDefault(where)
|
||||||
&& x.UserId == userId
|
&& x.UserId == userId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<EpisodeWatchStatus> SetEpisodeStatus(
|
public async Task<EpisodeWatchStatus?> SetEpisodeStatus(
|
||||||
int episodeId,
|
int episodeId,
|
||||||
int userId,
|
int userId,
|
||||||
WatchStatus status,
|
WatchStatus status,
|
||||||
int? watchedTime)
|
int? watchedTime)
|
||||||
{
|
{
|
||||||
Episode episode = await _episodes.Get(episodeId);
|
Episode episode = await _episodes.Get(episodeId);
|
||||||
|
int? percent = watchedTime != null && episode.Runtime > 0
|
||||||
|
? (int)Math.Round(watchedTime.Value / (episode.Runtime * 60f) * 100f)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (percent < MinWatchPercent)
|
||||||
|
return null;
|
||||||
|
if (percent > MaxWatchPercent)
|
||||||
|
{
|
||||||
|
status = WatchStatus.Completed;
|
||||||
|
watchedTime = null;
|
||||||
|
percent = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (watchedTime.HasValue && status != WatchStatus.Watching)
|
if (watchedTime.HasValue && status != WatchStatus.Watching)
|
||||||
throw new ValidationException("Can't have a watched time if the status is not watching.");
|
throw new ValidationException("Can't have a watched time if the status is not watching.");
|
||||||
|
|
||||||
EpisodeWatchStatus ret = new()
|
EpisodeWatchStatus ret = new()
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
EpisodeId = episodeId,
|
EpisodeId = episodeId,
|
||||||
Status = status,
|
Status = status,
|
||||||
WatchedTime = watchedTime,
|
WatchedTime = watchedTime,
|
||||||
|
WatchedPercent = percent,
|
||||||
};
|
};
|
||||||
await _database.EpisodeWatchInfo.Upsert(ret).RunAsync();
|
await _database.EpisodeWatchStatus.Upsert(ret)
|
||||||
|
.UpdateIf(x => !(status == WatchStatus.Watching && x.Status == WatchStatus.Completed))
|
||||||
|
.RunAsync();
|
||||||
await SetShowStatus(episode.ShowId, userId, WatchStatus.Watching);
|
await SetShowStatus(episode.ShowId, userId, WatchStatus.Watching);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task DeleteEpisodeStatus(
|
||||||
|
Expression<Func<Episode, bool>> where,
|
||||||
|
int userId)
|
||||||
|
{
|
||||||
|
await _database.EpisodeWatchStatus
|
||||||
|
.Where(x => x.Episode == _database.Episodes.FirstOrDefault(where)
|
||||||
|
&& x.UserId == userId)
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,6 @@ namespace Kyoo.Core.Api
|
|||||||
/// <response code="404">No movie with the given ID or slug could be found.</response>
|
/// <response code="404">No movie with the given ID or slug could be found.</response>
|
||||||
[HttpGet("{identifier:id}/watchStatus")]
|
[HttpGet("{identifier:id}/watchStatus")]
|
||||||
[HttpGet("{identifier:id}/watchStatus", Order = AlternativeRoute)]
|
[HttpGet("{identifier:id}/watchStatus", Order = AlternativeRoute)]
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
[UserOnly]
|
[UserOnly]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
@ -187,16 +186,17 @@ namespace Kyoo.Core.Api
|
|||||||
/// <param name="watchedTime">Where the user stopped watching.</param>
|
/// <param name="watchedTime">Where the user stopped watching.</param>
|
||||||
/// <returns>The newly set status.</returns>
|
/// <returns>The newly set status.</returns>
|
||||||
/// <response code="200">The status has been set</response>
|
/// <response code="200">The status has been set</response>
|
||||||
|
/// <response code="204">The status was not considered impactfull enough to be saved (less then 5% of watched for example).</response>
|
||||||
/// <response code="400">WatchedTime can't be specified if status is not watching.</response>
|
/// <response code="400">WatchedTime can't be specified if status is not watching.</response>
|
||||||
/// <response code="404">No movie with the given ID or slug could be found.</response>
|
/// <response code="404">No movie with the given ID or slug could be found.</response>
|
||||||
[HttpGet("{identifier:id}/watchStatus")]
|
[HttpPost("{identifier:id}/watchStatus")]
|
||||||
[HttpGet("{identifier:id}/watchStatus", Order = AlternativeRoute)]
|
[HttpPost("{identifier:id}/watchStatus", Order = AlternativeRoute)]
|
||||||
[PartialPermission(Kind.Read)]
|
|
||||||
[UserOnly]
|
[UserOnly]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<MovieWatchStatus> SetWatchStatus(Identifier identifier, WatchStatus status, int? watchedTime)
|
public async Task<MovieWatchStatus?> SetWatchStatus(Identifier identifier, WatchStatus status, int? watchedTime)
|
||||||
{
|
{
|
||||||
int id = await identifier.Match(
|
int id = await identifier.Match(
|
||||||
id => Task.FromResult(id),
|
id => Task.FromResult(id),
|
||||||
@ -209,5 +209,28 @@ namespace Kyoo.Core.Api
|
|||||||
watchedTime
|
watchedTime
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete watch status
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Delete watch status (to rewatch for example).
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="identifier">The ID or slug of the <see cref="Movie"/>.</param>
|
||||||
|
/// <returns>The newly set status.</returns>
|
||||||
|
/// <response code="204">The status has been deleted.</response>
|
||||||
|
/// <response code="404">No movie with the given ID or slug could be found.</response>
|
||||||
|
[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.DeleteMovieStatus(
|
||||||
|
identifier.IsSame<Movie>(),
|
||||||
|
User.GetId()!.Value
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,11 +100,11 @@ namespace Kyoo.Postgresql
|
|||||||
// /// </summary>
|
// /// </summary>
|
||||||
// public DbSet<PeopleRole> PeopleRoles { get; set; }
|
// public DbSet<PeopleRole> PeopleRoles { get; set; }
|
||||||
|
|
||||||
public DbSet<MovieWatchStatus> MovieWatchInfo { get; set; }
|
public DbSet<MovieWatchStatus> MovieWatchStatus { get; set; }
|
||||||
|
|
||||||
public DbSet<ShowWatchStatus> ShowWatchInfo { get; set; }
|
public DbSet<ShowWatchStatus> ShowWatchStatus { get; set; }
|
||||||
|
|
||||||
public DbSet<EpisodeWatchStatus> EpisodeWatchInfo { get; set; }
|
public DbSet<EpisodeWatchStatus> EpisodeWatchStatus { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a many to many link between two resources.
|
/// Add a many to many link between two resources.
|
||||||
|
@ -77,6 +77,8 @@ namespace Kyoo.Tests.Database
|
|||||||
|
|
||||||
LibraryManager = new LibraryManager(
|
LibraryManager = new LibraryManager(
|
||||||
libraryItem,
|
libraryItem,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
collection,
|
collection,
|
||||||
movies,
|
movies,
|
||||||
show,
|
show,
|
||||||
|
@ -49,7 +49,7 @@ namespace Kyoo.Tests
|
|||||||
.UseNpgsql(Connection)
|
.UseNpgsql(Connection)
|
||||||
.Options;
|
.Options;
|
||||||
|
|
||||||
using PostgresContext context = new(_options);
|
using PostgresContext context = new(_options, null);
|
||||||
context.Database.Migrate();
|
context.Database.Migrate();
|
||||||
|
|
||||||
using NpgsqlConnection conn = (NpgsqlConnection)context.Database.GetDbConnection();
|
using NpgsqlConnection conn = (NpgsqlConnection)context.Database.GetDbConnection();
|
||||||
@ -62,7 +62,7 @@ namespace Kyoo.Tests
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
using PostgresContext context = new(_options);
|
using PostgresContext context = new(_options, null);
|
||||||
context.Database.EnsureDeleted();
|
context.Database.EnsureDeleted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ namespace Kyoo.Tests
|
|||||||
|
|
||||||
public override DatabaseContext New()
|
public override DatabaseContext New()
|
||||||
{
|
{
|
||||||
return new PostgresContext(_context);
|
return new PostgresContext(_context, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override DbConnection NewConnection()
|
public override DbConnection NewConnection()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user