From a70760db9373b45e51481c5ebe7806a939745ff1 Mon Sep 17 00:00:00 2001
From: mctaylors <cantsendmails@mctaylors.ru>
Date: Sat, 23 Dec 2023 19:38:16 +0300
Subject: [PATCH] /play: Add playlist support

Signed-off-by: mctaylors <cantsendmails@mctaylors.ru>
---
 Cassette/Commands/ControlsCommandGroup.cs     | 38 ++++++++++++++++++-
 .../Extensions/LavalinkTrackExtensions.cs     |  2 +-
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/Cassette/Commands/ControlsCommandGroup.cs b/Cassette/Commands/ControlsCommandGroup.cs
index b5fc768..cba5486 100644
--- a/Cassette/Commands/ControlsCommandGroup.cs
+++ b/Cassette/Commands/ControlsCommandGroup.cs
@@ -5,6 +5,7 @@ using JetBrains.Annotations;
 using Lavalink4NET;
 using Lavalink4NET.Players.Queued;
 using Lavalink4NET.Rest.Entities.Tracks;
+using Lavalink4NET.Tracks;
 using Remora.Commands.Attributes;
 using Remora.Commands.Groups;
 using Remora.Discord.API.Abstractions.Objects;
@@ -26,7 +27,7 @@ public sealed class ControlsCommandGroup(
     FeedbackService feedbackService) : CommandGroup
 {
     [Command("play")]
-    [Description("Plays or adds the requested track to the queue")]
+    [Description("Plays or adds the requested track/playlist to the queue")]
     [RequireBotDiscordPermissions(DiscordPermission.Connect)]
     [DiscordDefaultDMPermission(false)]
     [UsedImplicitly]
@@ -39,7 +40,23 @@ public sealed class ControlsCommandGroup(
             return Result.FromSuccess();
         }
 
-        var track = await audioService.Tracks.LoadTrackAsync(query, TrackSearchMode.YouTube);
+        var loadResult = await audioService.Tracks.LoadTracksAsync(query, TrackSearchMode.YouTube);
+        if (!loadResult.IsSuccess)
+        {
+            return await feedbackService.SendContextualMessageResult(
+                "Track loading error, try again later.", feedbackService.Theme.FaultOrDanger);
+        }
+
+        if (loadResult.IsPlaylist)
+        {
+            return await PlayListResultAsync(player, loadResult);
+        }
+
+        return await PlayTrackResultAsync(player, loadResult.Track);
+    }
+
+    private async Task<Result> PlayTrackResultAsync(IQueuedLavalinkPlayer player, LavalinkTrack? track)
+    {
         if (track is null)
         {
             return await feedbackService.SendContextualMessageResult(
@@ -64,6 +81,23 @@ public sealed class ControlsCommandGroup(
             message.ToString(), feedbackService.Theme.Success);
     }
 
+    private async Task<Result> PlayListResultAsync(IQueuedLavalinkPlayer player, TrackLoadResult loadResult)
+    {
+        var playlistInfo = loadResult.Playlist;
+        if (playlistInfo is null)
+        {
+            return Result.FromSuccess();
+        }
+
+        foreach (var track in loadResult.Tracks)
+        {
+            await player.PlayAsync(track);
+        }
+
+        return await feedbackService.SendContextualMessageResult(
+            $"Added {Markdown.Bold(playlistInfo.Name)} playlist to the queue", feedbackService.Theme.Success);
+    }
+
     [Command("pause")]
     [Description("Pauses the current track")]
     [DiscordDefaultDMPermission(false)]
diff --git a/Cassette/Extensions/LavalinkTrackExtensions.cs b/Cassette/Extensions/LavalinkTrackExtensions.cs
index e2dfe15..933203f 100644
--- a/Cassette/Extensions/LavalinkTrackExtensions.cs
+++ b/Cassette/Extensions/LavalinkTrackExtensions.cs
@@ -7,7 +7,7 @@ namespace Cassette.Extensions;
 
 public static class LavalinkTrackExtensions
 {
-    public static string Display(this LavalinkTrack track, bool detailed = false)
+    public static string Display(this LavalinkTrack? track, bool detailed = false)
     {
         var builder = new StringBuilder();