diff --git a/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs index 7c714322..6084bcd0 100644 --- a/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs +++ b/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs @@ -17,15 +17,17 @@ // along with Kyoo. If not, see . using System; +using System.Collections.Generic; using System.Threading.Tasks; using Kyoo.Abstractions.Models; +using Kyoo.Abstractions.Models.Utils; namespace Kyoo.Abstractions.Controllers; /// /// A local repository to handle watched items /// -public interface IWatchStatusRepository : IRepository +public interface IWatchStatusRepository { // /// // /// The event handler type for all events of this repository. @@ -34,6 +36,10 @@ public interface IWatchStatusRepository : IRepository // /// A representing the asynchronous operation. // public delegate Task ResourceEventHandler(T resource); + Task> GetAll( + Include? include = default, + Pagination? limit = default); + Task GetMovieStatus(Guid movieId, Guid userId); Task SetMovieStatus(Guid movieId, Guid userId, WatchStatus status, int? watchedTime); diff --git a/back/src/Kyoo.Abstractions/Models/IWatchlist.cs b/back/src/Kyoo.Abstractions/Models/IWatchlist.cs index 498e2970..1c3e61ec 100644 --- a/back/src/Kyoo.Abstractions/Models/IWatchlist.cs +++ b/back/src/Kyoo.Abstractions/Models/IWatchlist.cs @@ -27,5 +27,5 @@ namespace Kyoo.Abstractions.Models; [OneOf(Types = new[] { typeof(Show), typeof(Movie), typeof(Episode) })] public interface IWatchlist : IResource, IThumbnails, IMetadata, IAddedDate, IQuery { - static Sort IQuery.DefaultSort => new Sort.By(nameof(AddedDate), true); + static Sort IQuery.DefaultSort => new Sort.By(nameof(AddedDate), true); } diff --git a/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs b/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs index e9f1b869..50c7ed25 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/DapperHelper.cs @@ -36,7 +36,6 @@ using Kyoo.Abstractions.Models.Utils; using Kyoo.Authentication; using Kyoo.Utils; using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; namespace Kyoo.Core.Controllers; @@ -354,7 +353,7 @@ public static class DapperHelper Filter? filter) where T : class, IResource { - InterpolatedSql.Dapper.SqlBuilders.SqlBuilder query = new(db, command); + SqlBuilder query = new(db, command); if (filter != null) query += ProcessFilter(filter, config); diff --git a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs index a418fe30..de7bd406 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs @@ -25,13 +25,14 @@ using System.Linq; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; +using Kyoo.Abstractions.Models.Exceptions; using Kyoo.Abstractions.Models.Utils; using Kyoo.Postgresql; using Microsoft.EntityFrameworkCore; namespace Kyoo.Core.Controllers; -public class WatchStatusRepository : DapperRepository, IWatchStatusRepository +public class WatchStatusRepository : IWatchStatusRepository { /// /// If the watch percent is below this value, don't consider the item started. @@ -55,21 +56,24 @@ public class WatchStatusRepository : DapperRepository, IWatchStatusR private readonly DatabaseContext _database; private readonly IRepository _episodes; private readonly IRepository _movies; + private readonly DbConnection _db; + private readonly SqlVariableContext _context; public WatchStatusRepository(DatabaseContext database, IRepository episodes, IRepository movies, DbConnection db, SqlVariableContext context) - : base(db, context) { _database = database; _episodes = episodes; _movies = movies; + _db = db; + _context = context; } // language=PostgreSQL - protected override FormattableString Sql => $""" + protected FormattableString Sql => $""" select s.*, m.*, @@ -77,35 +81,53 @@ public class WatchStatusRepository : DapperRepository, IWatchStatusR /* includes */ from ( select - s.* -- Show as s + s.*, -- Show as s + sw.*, + sw.added_date as order, + sw.status as watch_status from shows as s inner join show_watch_status as sw on sw.show_id = s.id and sw.user_id = [current_user]) as s full outer join ( select - m.* -- Movie as m + m.*, -- Movie as m + mw.*, + mw.added_date as order, + mw.status as watch_status from movies as m inner join movie_watch_status as mw on mw.movie_id = m.id - and mw.user_id = [current_user]) as s) as m + and mw.user_id = [current_user]) as m on false full outer join ( select - e.* -- Episode as e + e.*, -- Episode as e + ew.*, + ew.added_date as order, + ew.status as watch_status from - episode as es + episodes as e inner join episode_watch_status as ew on ew.episode_id = e.id - and ew.user_id = [current_user])) as e + and ew.user_id = [current_user]) as e on false + where + coalesce(s.watch_status, m.watch_status, e.watch_status) = 'watching'::watch_status + or coalesce(s.watch_status, m.watch_status, e.watch_status) = 'completed'::watch_status + order by + coalesce(s.order, m.order, e.order) desc, + coalesce(s.id, m.id, e.id) asc """; - protected override Dictionary Config => new() + protected Dictionary Config => new() { { "s", typeof(Show) }, + { "sw", typeof(ShowWatchStatus) }, { "m", typeof(Movie) }, + { "mw", typeof(MovieWatchStatus) }, { "e", typeof(Episode) }, + { "ew", typeof(EpisodeWatchStatus) }, }; - protected override IWatchlist Mapper(List items) + protected IWatchlist Mapper(List items) { if (items[0] is Show show && show.Id != Guid.Empty) return show; @@ -116,6 +138,46 @@ public class WatchStatusRepository : DapperRepository, IWatchStatusR throw new InvalidDataException(); } + /// + public virtual async Task Get(Guid id, Include? include = default) + { + IWatchlist? ret = await GetOrDefault(id, include); + if (ret == null) + throw new ItemNotFoundException($"No {nameof(IWatchlist)} found with the id {id}"); + return ret; + } + + /// + public Task GetOrDefault(Guid id, Include? include = null) + { + return _db.QuerySingle( + Sql, + Config, + Mapper, + _context, + include, + new Filter.Eq(nameof(IResource.Id), id) + ); + } + + /// + public Task> GetAll( + Include? include = default, + Pagination? limit = default) + { + return _db.Query( + Sql, + Config, + Mapper, + (id) => Get(id), + _context, + include, + null, + null, + limit ?? new() + ); + } + /// public Task GetMovieStatus(Guid movieId, Guid userId) { diff --git a/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs b/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs index ca17b04b..deead782 100644 --- a/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/WatchlistApi.cs @@ -51,8 +51,6 @@ namespace Kyoo.Core.Api /// /// Get all resources that match the given filter. /// - /// Sort information about the query (sort by, sort order). - /// Filter the returned items. /// How many items per page should be returned, where should the page start... /// The aditional fields to include in the result. /// A list of resources that match every filters. @@ -62,14 +60,10 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] public async Task>> GetAll( - [FromQuery] Sort sortBy, - [FromQuery] Filter? filter, [FromQuery] Pagination pagination, [FromQuery] Include? fields) { ICollection resources = await _repository.GetAll( - filter, - sortBy, fields, pagination );