From b9c8f57732ed520dbddc271894310e31c336b231 Mon Sep 17 00:00:00 2001 From: Angelo Breuer Date: Tue, 27 Feb 2024 19:16:49 +0100 Subject: [PATCH] feat(sponsorblock/chapters): Add SponsorBlock chapters model --- .../Chapter.cs | 9 +++ .../Events/ChapterStartedEventArgs.cs | 18 ++++++ .../Events/ChaptersLoadedEventArgs.cs | 17 ++++++ .../Payloads/ChapterStartedEventPayload.cs | 19 ++++++ .../Payloads/ChaptersLoadedEventPayload.cs | 26 +++++--- .../Players/ISponsorBlockPlayerListener.cs | 4 ++ .../SponsorBlockIntegration.cs | 60 +++++++++++++++++++ .../SponsorBlockJsonSerializerContext.cs | 2 + 8 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 src/Lavalink4NET.Integrations.SponsorBlock/Chapter.cs create mode 100644 src/Lavalink4NET.Integrations.SponsorBlock/Events/ChapterStartedEventArgs.cs create mode 100644 src/Lavalink4NET.Integrations.SponsorBlock/Events/ChaptersLoadedEventArgs.cs create mode 100644 src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChapterStartedEventPayload.cs diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/Chapter.cs b/src/Lavalink4NET.Integrations.SponsorBlock/Chapter.cs new file mode 100644 index 00000000..5175c65f --- /dev/null +++ b/src/Lavalink4NET.Integrations.SponsorBlock/Chapter.cs @@ -0,0 +1,9 @@ +namespace Lavalink4NET.Integrations.SponsorBlock; + +using System; + +public sealed record class Chapter( + string Name, + TimeSpan Start, + TimeSpan End, + TimeSpan Duration); \ No newline at end of file diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/Events/ChapterStartedEventArgs.cs b/src/Lavalink4NET.Integrations.SponsorBlock/Events/ChapterStartedEventArgs.cs new file mode 100644 index 00000000..0d476c8b --- /dev/null +++ b/src/Lavalink4NET.Integrations.SponsorBlock/Events/ChapterStartedEventArgs.cs @@ -0,0 +1,18 @@ +namespace Lavalink4NET.Integrations.SponsorBlock.Events; + +using System; + +public sealed class ChapterStartedEventArgs +{ + public ChapterStartedEventArgs(ulong guildId, Chapter chapter) + { + ArgumentNullException.ThrowIfNull(chapter); + + GuildId = guildId; + Chapter = chapter; + } + + public ulong GuildId { get; } + + public Chapter Chapter { get; } +} diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/Events/ChaptersLoadedEventArgs.cs b/src/Lavalink4NET.Integrations.SponsorBlock/Events/ChaptersLoadedEventArgs.cs new file mode 100644 index 00000000..c630bd7b --- /dev/null +++ b/src/Lavalink4NET.Integrations.SponsorBlock/Events/ChaptersLoadedEventArgs.cs @@ -0,0 +1,17 @@ +namespace Lavalink4NET.Integrations.SponsorBlock.Events; + +using System; +using System.Collections.Immutable; + +public sealed class ChaptersLoadedEventArgs : EventArgs +{ + public ChaptersLoadedEventArgs(ulong guildId, ImmutableArray chapters) + { + GuildId = guildId; + Chapters = chapters; + } + + public ulong GuildId { get; } + + public ImmutableArray Chapters { get; } +} diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChapterStartedEventPayload.cs b/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChapterStartedEventPayload.cs new file mode 100644 index 00000000..e943755c --- /dev/null +++ b/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChapterStartedEventPayload.cs @@ -0,0 +1,19 @@ +namespace Lavalink4NET.Integrations.SponsorBlock.Payloads; + +using System.Text.Json.Serialization; +using Lavalink4NET.Protocol.Converters; +using Lavalink4NET.Protocol.Payloads; + +public sealed record class ChapterStartedEventPayload( +#if NET7_0_OR_GREATER + [property: JsonRequired] +#endif + [property: JsonPropertyName("guildId")] + [property: JsonConverter(typeof(SnowflakeJsonConverter))] + ulong GuildId, + +#if NET7_0_OR_GREATER + [property: JsonRequired] +#endif + [property: JsonPropertyName("chapter")] + ChapterModel Chapter) : IEventPayload; \ No newline at end of file diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChaptersLoadedEventPayload.cs b/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChaptersLoadedEventPayload.cs index 4002f066..3a164081 100644 --- a/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChaptersLoadedEventPayload.cs +++ b/src/Lavalink4NET.Integrations.SponsorBlock/Payloads/ChaptersLoadedEventPayload.cs @@ -1,10 +1,20 @@ namespace Lavalink4NET.Integrations.SponsorBlock.Payloads; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -internal class ChaptersLoadedEventPayload -{ -} +using System.Collections.Immutable; +using System.Text.Json.Serialization; +using Lavalink4NET.Protocol.Converters; +using Lavalink4NET.Protocol.Payloads; + +public sealed record class ChaptersLoadedEventPayload( +#if NET7_0_OR_GREATER + [property: JsonRequired] +#endif + [property: JsonPropertyName("guildId")] + [property: JsonConverter(typeof(SnowflakeJsonConverter))] + ulong GuildId, + +#if NET7_0_OR_GREATER + [property: JsonRequired] +#endif + [property: JsonPropertyName("chapters")] + ImmutableArray Chapters) : IEventPayload; \ No newline at end of file diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/Players/ISponsorBlockPlayerListener.cs b/src/Lavalink4NET.Integrations.SponsorBlock/Players/ISponsorBlockPlayerListener.cs index 77249a2c..f47e6405 100644 --- a/src/Lavalink4NET.Integrations.SponsorBlock/Players/ISponsorBlockPlayerListener.cs +++ b/src/Lavalink4NET.Integrations.SponsorBlock/Players/ISponsorBlockPlayerListener.cs @@ -10,4 +10,8 @@ public interface ISponsorBlockPlayerListener : ILavalinkPlayerListener ValueTask NotifySegmentSkippedAsync(Segment segment, CancellationToken cancellationToken = default); ValueTask NotifySegmentsLoadedAsync(ImmutableArray segments, CancellationToken cancellationToken = default); + + ValueTask NotifyChapterStartedAsync(Chapter chapter, CancellationToken cancellationToken = default); + + ValueTask NotifyChaptersLoadedAsync(ImmutableArray chapters, CancellationToken cancellationToken = default); } diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockIntegration.cs b/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockIntegration.cs index 21a20470..54ad5022 100644 --- a/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockIntegration.cs +++ b/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockIntegration.cs @@ -22,6 +22,8 @@ static SponsorBlockIntegration() // Register events PayloadJsonConverter.RegisterEvent("SegmentsLoaded", SponsorBlockJsonSerializerContext.Default.SegmentsLoadedEventPayload); PayloadJsonConverter.RegisterEvent("SegmentSkipped", SponsorBlockJsonSerializerContext.Default.SegmentSkippedEventPayload); + PayloadJsonConverter.RegisterEvent("ChaptersLoaded", SponsorBlockJsonSerializerContext.Default.ChaptersLoadedEventPayload); + PayloadJsonConverter.RegisterEvent("ChapterStarted", SponsorBlockJsonSerializerContext.Default.ChapterStartedEventPayload); } public SponsorBlockIntegration(IAudioService audioService) @@ -35,6 +37,10 @@ public SponsorBlockIntegration(IAudioService audioService) public event AsyncEventHandler? SegmentsLoaded; + public event AsyncEventHandler? ChapterStarted; + + public event AsyncEventHandler? ChaptersLoaded; + async ValueTask ILavalinkIntegration.ProcessPayloadAsync(IPayload payload, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -45,6 +51,12 @@ async ValueTask ILavalinkIntegration.ProcessPayloadAsync(IPayload payload, Cance StartOffset: model.StartOffset, EndOffset: model.EndOffset); + static Chapter CreateChapter(ChapterModel model) => new( + Name: model.Name, + Start: model.Start, + End: model.End, + Duration: model.Duration); + if (payload is SegmentSkippedEventPayload segmentSkippedEventPayload) { var segment = CreateSegment(segmentSkippedEventPayload.Segment); @@ -92,5 +104,53 @@ await playerListener .ConfigureAwait(false); } } + + if (payload is ChaptersLoadedEventPayload chaptersLoadedEventPayload) + { + var chapters = chaptersLoadedEventPayload.Chapters.Select(CreateChapter).ToImmutableArray(); + + var eventArgs = new ChaptersLoadedEventArgs( + guildId: chaptersLoadedEventPayload.GuildId, + chapters: chapters); + + await ChaptersLoaded + .InvokeAsync(this, eventArgs) + .ConfigureAwait(false); + + var player = await _audioService.Players + .GetPlayerAsync(chaptersLoadedEventPayload.GuildId, cancellationToken) + .ConfigureAwait(false); + + if (player is ISponsorBlockPlayerListener playerListener) + { + await playerListener + .NotifyChaptersLoadedAsync(chapters, cancellationToken) + .ConfigureAwait(false); + } + } + + if (payload is ChapterStartedEventPayload chapterStartedEventPayload) + { + var chapter = CreateChapter(chapterStartedEventPayload.Chapter); + + var eventArgs = new ChapterStartedEventArgs( + guildId: chapterStartedEventPayload.GuildId, + chapter: chapter); + + await ChapterStarted + .InvokeAsync(this, eventArgs) + .ConfigureAwait(false); + + var player = await _audioService.Players + .GetPlayerAsync(chapterStartedEventPayload.GuildId, cancellationToken) + .ConfigureAwait(false); + + if (player is ISponsorBlockPlayerListener playerListener) + { + await playerListener + .NotifyChapterStartedAsync(chapter, cancellationToken) + .ConfigureAwait(false); + } + } } } diff --git a/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockJsonSerializerContext.cs b/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockJsonSerializerContext.cs index 9e71c05e..f08fff97 100644 --- a/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockJsonSerializerContext.cs +++ b/src/Lavalink4NET.Integrations.SponsorBlock/SponsorBlockJsonSerializerContext.cs @@ -7,6 +7,8 @@ [JsonSerializable(typeof(ImmutableArray))] [JsonSerializable(typeof(SegmentsLoadedEventPayload))] [JsonSerializable(typeof(SegmentSkippedEventPayload))] +[JsonSerializable(typeof(ChaptersLoadedEventPayload))] +[JsonSerializable(typeof(ChapterStartedEventPayload))] internal sealed partial class SponsorBlockJsonSerializerContext : JsonSerializerContext { }