diff --git a/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs index 23d6e6c6..a4372d23 100644 --- a/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs +++ b/back/src/Kyoo.Abstractions/Controllers/IWatchStatusRepository.cs @@ -30,7 +30,6 @@ namespace Kyoo.Abstractions.Controllers; public interface IWatchStatusRepository { public delegate Task ResourceEventHandler(T resource); - public delegate Task WatchStatusDeletedEventHandler(Guid resourceId, Guid userId); Task> GetAll( Filter? filter = default, @@ -54,10 +53,6 @@ public interface IWatchStatusRepository Task DeleteMovieStatus(Guid movieId, Guid userId); - static event WatchStatusDeletedEventHandler OnMovieStatusDeletedHandler; - protected static Task OnMovieStatusDeleted(Guid movieId, Guid userId) => - OnMovieStatusDeletedHandler?.Invoke(movieId, userId) ?? Task.CompletedTask; - Task GetShowStatus(Guid showId, Guid userId); Task SetShowStatus(Guid showId, Guid userId, WatchStatus status); @@ -68,10 +63,6 @@ public interface IWatchStatusRepository Task DeleteShowStatus(Guid showId, Guid userId); - static event WatchStatusDeletedEventHandler OnShowStatusDeletedHandler; - protected static Task OnShowStatusDeleted(Guid showId, Guid userId) => - OnShowStatusDeletedHandler?.Invoke(showId, userId) ?? Task.CompletedTask; - Task GetEpisodeStatus(Guid episodeId, Guid userId); /// Where the user has stopped watching. Only usable if Status @@ -89,9 +80,4 @@ public interface IWatchStatusRepository OnEpisodeStatusChangedHandler?.Invoke(obj) ?? Task.CompletedTask; Task DeleteEpisodeStatus(Guid episodeId, Guid userId); - - static event WatchStatusDeletedEventHandler OnEpisodeStatusDeletedHandler; - protected static Task OnEpisodeStatusDeleted(Guid episodeId, Guid userId) => - OnEpisodeStatusDeletedHandler?.Invoke(episodeId, userId) ?? Task.CompletedTask; - } diff --git a/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs b/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs index 928cd640..1caf380c 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs @@ -46,13 +46,27 @@ public enum WatchStatus /// The user has not started watching this but plans to. /// Planned, + + /// + /// The watch status was deleted and can not be retrived again. + /// + Deleted, } +public interface IWatchStatus +{ + /// + /// Has the user started watching, is it planned? + /// + public WatchStatus Status { get; set; } +} + + /// /// Metadata of what an user as started/planned to watch. /// [SqlFirstColumn(nameof(UserId))] -public class MovieWatchStatus : IAddedDate +public class MovieWatchStatus : IAddedDate, IWatchStatus { /// /// The ID of the user that started watching this episode. @@ -107,7 +121,7 @@ public class MovieWatchStatus : IAddedDate } [SqlFirstColumn(nameof(UserId))] -public class EpisodeWatchStatus : IAddedDate +public class EpisodeWatchStatus : IAddedDate, IWatchStatus { /// /// The ID of the user that started watching this episode. @@ -162,7 +176,7 @@ public class EpisodeWatchStatus : IAddedDate } [SqlFirstColumn(nameof(UserId))] -public class ShowWatchStatus : IAddedDate +public class ShowWatchStatus : IAddedDate, IWatchStatus { /// /// The ID of the user that started watching this episode. diff --git a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs index 1465d938..d8a72070 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/WatchStatusRepository.cs @@ -38,7 +38,7 @@ public class WatchStatusRepository( IRepository movies, DbConnection db, SqlVariableContext context - ) : IWatchStatusRepository +) : IWatchStatusRepository { /// /// If the watch percent is below this value, don't consider the item started. @@ -275,7 +275,15 @@ public class WatchStatusRepository( await database .MovieWatchStatus.Where(x => x.MovieId == movieId && x.UserId == userId) .ExecuteDeleteAsync(); - await IWatchStatusRepository.OnMovieStatusDeleted(movieId, userId); + await IWatchStatusRepository.OnMovieStatusChanged( + new() + { + UserId = userId, + MovieId = movieId, + AddedDate = DateTime.UtcNow, + Status = WatchStatus.Deleted, + } + ); } /// @@ -419,7 +427,15 @@ public class WatchStatusRepository( await database .EpisodeWatchStatus.Where(x => x.Episode.ShowId == showId && x.UserId == userId) .ExecuteDeleteAsync(); - await IWatchStatusRepository.OnShowStatusDeleted(showId, userId); + await IWatchStatusRepository.OnShowStatusChanged( + new() + { + UserId = userId, + ShowId = showId, + AddedDate = DateTime.UtcNow, + Status = WatchStatus.Deleted, + } + ); } /// @@ -490,6 +506,14 @@ public class WatchStatusRepository( await database .EpisodeWatchStatus.Where(x => x.EpisodeId == episodeId && x.UserId == userId) .ExecuteDeleteAsync(); - await IWatchStatusRepository.OnEpisodeStatusDeleted(episodeId, userId); + await IWatchStatusRepository.OnEpisodeStatusChanged( + new() + { + UserId = userId, + EpisodeId = episodeId, + AddedDate = DateTime.UtcNow, + Status = WatchStatus.Deleted, + } + ); } } diff --git a/back/src/Kyoo.RabbitMq/Message.cs b/back/src/Kyoo.RabbitMq/Message.cs new file mode 100644 index 00000000..a1bbba9b --- /dev/null +++ b/back/src/Kyoo.RabbitMq/Message.cs @@ -0,0 +1,39 @@ +// 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.Text; +using System.Text.Json; + +namespace Kyoo.RabbitMq; + +public class Message +{ + public string Action { get; set; } + public string Type { get; set; } + public object Value { get; set; } + + public string AsRoutingKey() + { + return $"{Type}.{Action}"; + } + + public byte[] AsBytes() + { + return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(this)); + } +} diff --git a/back/src/Kyoo.RabbitMq/RabbitProducer.cs b/back/src/Kyoo.RabbitMq/RabbitProducer.cs index 51959a0a..d04ffff7 100644 --- a/back/src/Kyoo.RabbitMq/RabbitProducer.cs +++ b/back/src/Kyoo.RabbitMq/RabbitProducer.cs @@ -16,8 +16,6 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using System.Text; -using System.Text.Json; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using RabbitMQ.Client; @@ -40,6 +38,16 @@ public class RabbitProducer _ListenResourceEvents("events.resource"); _ListenResourceEvents("events.resource"); _ListenResourceEvents("events.resource"); + + _channel.ExchangeDeclare("events.watched", ExchangeType.Topic); + IWatchStatusRepository.OnMovieStatusChangedHandler += _PublishWatchStatus( + "movie" + ); + IWatchStatusRepository.OnShowStatusChangedHandler += _PublishWatchStatus( + "show" + ); + IWatchStatusRepository.OnEpisodeStatusChangedHandler += + _PublishWatchStatus("episode"); } private void _ListenResourceEvents(string exchange) @@ -61,16 +69,38 @@ public class RabbitProducer { return (T resource) => { - var message = new - { - Action = action, - Type = type, - Resource = resource, - }; + Message message = + new() + { + Action = action, + Type = type, + Value = resource, + }; _channel.BasicPublish( exchange, - routingKey: $"{type}.{action}", - body: Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message)) + routingKey: message.AsRoutingKey(), + body: message.AsBytes() + ); + return Task.CompletedTask; + }; + } + + private IWatchStatusRepository.ResourceEventHandler _PublishWatchStatus(string resource) + where T : IWatchStatus + { + return (status) => + { + Message message = + new() + { + Type = resource, + Action = status.Status.ToString().ToLowerInvariant(), + Value = status, + }; + _channel.BasicPublish( + exchange: "events.watched", + routingKey: message.AsRoutingKey(), + body: message.AsBytes() ); return Task.CompletedTask; };