diff --git a/back/Kyoo.ruleset b/back/Kyoo.ruleset
index 60883533..82ed916b 100644
--- a/back/Kyoo.ruleset
+++ b/back/Kyoo.ruleset
@@ -28,7 +28,10 @@
+
+
+
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
index 6d7ab289..8adc3ca4 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs
@@ -238,6 +238,17 @@ namespace Kyoo.Abstractions.Models
|| (x.SeasonNumber == SeasonNumber && x.EpisodeNumber > EpisodeNumber)
);
+ [SerializeIgnore] public ICollection Watched { get; set; }
+
+ ///
+ /// Metadata of what an user as started/planned to watch.
+ ///
+ [Projectable(UseMemberBody = nameof(_WatchInfo), OnlyOnInclude = true)]
+ [LoadableRelation] public EpisodeWatchInfo? WatchInfo { get; set; }
+
+ // There is a global query filter to filter by user so we just need to do single.
+ private EpisodeWatchInfo? _WatchInfo => Watched.FirstOrDefault();
+
///
/// Links to watch this episode.
///
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs b/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
index 8723d73f..71377ca8 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
@@ -146,16 +146,16 @@ namespace Kyoo.Abstractions.Models
Hls = $"/video/movie/{Slug}/master.m3u8",
};
- [SerializeIgnore] public ICollection Watched { get; set; }
+ [SerializeIgnore] public ICollection Watched { get; set; }
///
/// Metadata of what an user as started/planned to watch.
///
[Projectable(UseMemberBody = nameof(_WatchInfo), OnlyOnInclude = true)]
- [LoadableRelation] public WatchInfo? WatchInfo { get; set; }
+ [LoadableRelation] public MovieWatchInfo? WatchInfo { get; set; }
// There is a global query filter to filter by user so we just need to do single.
- private WatchInfo? _WatchInfo => Watched.FirstOrDefault();
+ private MovieWatchInfo? _WatchInfo => Watched.FirstOrDefault();
///
public void OnMerge(object merged)
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs
index a6a5fed9..8363b7d1 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs
@@ -178,6 +178,17 @@ namespace Kyoo.Abstractions.Models
.ThenBy(x => x.EpisodeNumber)
.FirstOrDefault();
+ [SerializeIgnore] public ICollection Watched { get; set; }
+
+ ///
+ /// Metadata of what an user as started/planned to watch.
+ ///
+ [Projectable(UseMemberBody = nameof(_WatchInfo), OnlyOnInclude = true)]
+ [LoadableRelation] public ShowWatchInfo? WatchInfo { get; set; }
+
+ // There is a global query filter to filter by user so we just need to do single.
+ private ShowWatchInfo? _WatchInfo => Watched.FirstOrDefault();
+
///
public void OnMerge(object merged)
{
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/User.cs b/back/src/Kyoo.Abstractions/Models/Resources/User.cs
index 5328f381..b4f9913a 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/User.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/User.cs
@@ -69,11 +69,11 @@ namespace Kyoo.Abstractions.Models
///
public Image? Logo { get; set; }
- ///
- /// The user's watch list.
- ///
- [SerializeIgnore]
- public ICollection? Watchlist { get; set; }
+ // ///
+ // /// The user's watch list.
+ // ///
+ // // [SerializeIgnore]
+ // // public ICollection? Watchlist { get; set; }
public User() { }
diff --git a/back/src/Kyoo.Abstractions/Models/Resources/WatchInfo.cs b/back/src/Kyoo.Abstractions/Models/Resources/WatchInfo.cs
index 5a1a2834..c6ae4dc2 100644
--- a/back/src/Kyoo.Abstractions/Models/Resources/WatchInfo.cs
+++ b/back/src/Kyoo.Abstractions/Models/Resources/WatchInfo.cs
@@ -17,6 +17,8 @@
// along with Kyoo. If not, see .
using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
using Kyoo.Abstractions.Models.Attributes;
namespace Kyoo.Abstractions.Models
@@ -50,7 +52,46 @@ namespace Kyoo.Abstractions.Models
///
/// Metadata of what an user as started/planned to watch.
///
- public class WatchInfo : IAddedDate
+ public class MovieWatchInfo : IAddedDate
+ {
+ ///
+ /// The ID of the user that started watching this episode.
+ ///
+ [SerializeIgnore] public Guid UserId { get; set; }
+
+ ///
+ /// The user that started watching this episode.
+ ///
+ [SerializeIgnore] public User User { get; set; }
+
+ ///
+ /// The ID of the movie started.
+ ///
+ [SerializeIgnore] public Guid MovieId { get; set; }
+
+ ///
+ /// The started.
+ ///
+ [SerializeIgnore] public Movie Movie { get; set; }
+
+ ///
+ public DateTime AddedDate { get; set; }
+
+ ///
+ /// Has the user started watching, is it planned?
+ ///
+ public WatchStatus Status { get; set; }
+
+ ///
+ /// Where the player has stopped watching the movie (in seconds).
+ ///
+ ///
+ /// Null if the status is not Watching.
+ ///
+ public int? WatchedTime { get; set; }
+ }
+
+ public class EpisodeWatchInfo : IAddedDate
{
///
/// The ID of the user that started watching this episode.
@@ -70,20 +111,10 @@ namespace Kyoo.Abstractions.Models
///
/// The started.
///
- [SerializeIgnore] public Episode? Episode { get; set; }
-
- ///
- /// The ID of the movie started.
- ///
- [SerializeIgnore] public Guid? MovieId { get; set; }
-
- ///
- /// The started.
- ///
- [SerializeIgnore] public Movie? Movie { get; set; }
+ [SerializeIgnore] public Episode Episode { get; set; }
///
- [SerializeIgnore] public DateTime AddedDate { get; set; }
+ public DateTime AddedDate { get; set; }
///
/// Has the user started watching, is it planned?
@@ -98,4 +129,56 @@ namespace Kyoo.Abstractions.Models
///
public int? WatchedTime { get; set; }
}
+
+ public class ShowWatchInfo : IAddedDate
+ {
+ ///
+ /// The ID of the user that started watching this episode.
+ ///
+ [SerializeIgnore] public Guid UserId { get; set; }
+
+ ///
+ /// The user that started watching this episode.
+ ///
+ [SerializeIgnore] public User User { get; set; }
+
+ ///
+ /// The ID of the show started.
+ ///
+ [SerializeIgnore] public Guid ShowId { get; set; }
+
+ ///
+ /// The started.
+ ///
+ [SerializeIgnore] public Show Show { get; set; }
+
+ ///
+ public DateTime AddedDate { get; set; }
+
+ ///
+ /// Has the user started watching, is it planned?
+ ///
+ public WatchStatus Status { get; set; }
+
+ ///
+ /// The ID of the episode started.
+ ///
+ [SerializeIgnore] public Guid NextEpisodeId { get; set; }
+
+ ///
+ /// The started.
+ ///
+ public Episode? NextEpisode { get; set; }
+
+ ///
+ /// Where the player has stopped watching the episode (in seconds).
+ ///
+ ///
+ /// Null if the status is not Watching or if the next episode is not started.
+ ///
+ [Projectable(UseMemberBody = nameof(_WatchedTime), NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public int? WatchedTime { get; set; }
+
+ private int? _WatchedTime => NextEpisode?.Watched.FirstOrDefault()?.WatchedTime;
+ }
}
diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs
index f9d097b4..26bffbab 100644
--- a/back/src/Kyoo.Postgresql/DatabaseContext.cs
+++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs
@@ -100,7 +100,11 @@ namespace Kyoo.Postgresql
// ///
// public DbSet PeopleRoles { get; set; }
- public DbSet WatchInfo { get; set; }
+ public DbSet MovieWatchInfo { get; set; }
+
+ public DbSet ShowWatchInfo { get; set; }
+
+ public DbSet EpisodeWatchInfo { get; set; }
///
/// Add a many to many link between two resources.
@@ -302,10 +306,24 @@ namespace Kyoo.Postgresql
modelBuilder.Entity().OwnsOne(x => x.Logo);
- modelBuilder.Entity()
- .HasKey(x => new { User = x.UserId, Episode = x.EpisodeId, Movie = x.MovieId });
- modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId);
+ modelBuilder.Entity()
+ .HasKey(x => new { User = x.UserId, Movie = x.MovieId });
+ modelBuilder.Entity()
+ .HasKey(x => new { User = x.UserId, Show = x.ShowId });
+ modelBuilder.Entity()
+ .HasKey(x => new { User = x.UserId, Episode = x.EpisodeId });
+
+ modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId);
+ modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId);
+ modelBuilder.Entity().HasQueryFilter(x => x.UserId == CurrentUserId);
+
+ _HasAddedDate(modelBuilder);
+ _HasAddedDate(modelBuilder);
+ _HasAddedDate(modelBuilder);
+
modelBuilder.Entity().Ignore(x => x.WatchInfo);
+ modelBuilder.Entity().Ignore(x => x.WatchInfo);
+ modelBuilder.Entity().Ignore(x => x.WatchInfo);
modelBuilder.Entity()
.HasIndex(x => x.Slug)