From e3c722b403d30803843ce42b3db7e52c3b6f8152 Mon Sep 17 00:00:00 2001 From: "Frederik P." Date: Sun, 24 Mar 2024 00:50:22 +0100 Subject: [PATCH] Bunch of fixes --- Melodica/Services/Caching/MediaFileCache.cs | 1 + .../SoundCloud/AsyncSoundcloudDownloader.cs | 1 + .../Spotify/AsyncSpotifyDownloader.cs | 35 ++++++++++++++----- .../YouTube/AsyncYoutubeDownloader.cs | 17 +++++---- Melodica/Services/Playback/Jukebox.cs | 17 ++++----- Melodica/Services/Playback/JukeboxCommands.cs | 33 ++++++++--------- 6 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Melodica/Services/Caching/MediaFileCache.cs b/Melodica/Services/Caching/MediaFileCache.cs index ddfe275..1513143 100644 --- a/Melodica/Services/Caching/MediaFileCache.cs +++ b/Melodica/Services/Caching/MediaFileCache.cs @@ -8,6 +8,7 @@ namespace Melodica.Services.Caching; +//TODO: Also support storing cache for media info without actual data. public sealed class MediaFileCache : IMediaCache { public MediaFileCache(string dirName) diff --git a/Melodica/Services/Downloaders/SoundCloud/AsyncSoundcloudDownloader.cs b/Melodica/Services/Downloaders/SoundCloud/AsyncSoundcloudDownloader.cs index 29b8398..fd84975 100644 --- a/Melodica/Services/Downloaders/SoundCloud/AsyncSoundcloudDownloader.cs +++ b/Melodica/Services/Downloaders/SoundCloud/AsyncSoundcloudDownloader.cs @@ -81,6 +81,7 @@ static async Task DownloadPlaylistAsync(MediaInfo info) PlayableMediaStream? current = null; foreach (var track in playlist.Tracks) { + //TODO: Convert to lazy getter? var trackInfo = TrackToMediaInfo(track); var media = await CreatePlayableMediaAsync(trackInfo, info); if (first is null) diff --git a/Melodica/Services/Downloaders/Spotify/AsyncSpotifyDownloader.cs b/Melodica/Services/Downloaders/Spotify/AsyncSpotifyDownloader.cs index 1350b99..fbac7f7 100644 --- a/Melodica/Services/Downloaders/Spotify/AsyncSpotifyDownloader.cs +++ b/Melodica/Services/Downloaders/Spotify/AsyncSpotifyDownloader.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using Melodica.Config; using Melodica.Services.Caching; using Melodica.Services.Downloaders.Exceptions; +using Melodica.Services.Downloaders.YouTube; using Melodica.Services.Media; using Melodica.Utility; using SpotifyAPI.Web; @@ -146,15 +146,32 @@ static async Task GetPlaylistInfoAsync(ReadOnlyMemory url) static async ValueTask DownloadFromProviderAsync(MediaInfo info) { if (await cache.TryGetAsync(info.Id) is var cachedMedia && cachedMedia is not null) - { return cachedMedia; - } - var extInfo = await downloader.GetInfoAsync($"{info.Artist} {info.Title}".AsMemory()); - var extMedia = await downloader.DownloadAsync(extInfo); + static async Task DataGetter(MediaInfo info) + { + try + { + if (downloader is AsyncYoutubeDownloader yt) + { + var extInfo = await downloader.GetInfoAsync($"{info.Artist} {info.Title}".AsMemory()); + return await yt.GetMediaHttpStreamAsync(extInfo); + } + throw new NotSupportedException("Only YouTube fallback downloader is supported for this operation with Spotify."); + } + catch (Exception ex) + { + throw new MediaUnavailableException("Video was unavailable.", ex); + } + } - extMedia.SetInfo(info with { Id = extInfo.Id, Url = extInfo.Url }); - return extMedia; + var media = new PlayableMediaStream( + (Func>)DataGetter, + (Func>)(() => info.WrapTask()), + null, + cache + ); + return media; } static async ValueTask DownloadSpotifyAlbumAsync(FullAlbum album) @@ -194,6 +211,7 @@ static async ValueTask DownloadSpotifyPlaylistAsync(FullPla { var info = FullTrackToMediaInfo(track); var media = await DownloadFromProviderAsync(info); + if (first is null) { first = media; @@ -201,6 +219,7 @@ static async ValueTask DownloadSpotifyPlaylistAsync(FullPla continue; } current!.Next = media; + current = current.Next; } return first!; diff --git a/Melodica/Services/Downloaders/YouTube/AsyncYoutubeDownloader.cs b/Melodica/Services/Downloaders/YouTube/AsyncYoutubeDownloader.cs index 4301cec..3a30768 100644 --- a/Melodica/Services/Downloaders/YouTube/AsyncYoutubeDownloader.cs +++ b/Melodica/Services/Downloaders/YouTube/AsyncYoutubeDownloader.cs @@ -1,6 +1,4 @@ -using System.Net; -using System.Text; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using AngleSharp.Text; using Melodica.Services.Caching; using Melodica.Services.Downloaders.Exceptions; @@ -145,7 +143,14 @@ static async Task DownloadLivestream(MediaInfo info) return media; } - static async Task GetPlayableMediaFromInfoAsync(MediaInfo info) + internal async Task GetMediaHttpStreamAsync(MediaInfo info) + { + var manifest = await yt.Videos.Streams.GetManifestAsync(info.Id); + var streamInfo = manifest.GetAudioOnlyStreams().GetWithHighestBitrate() ?? throw new NullReferenceException("Could not get stream from YouTube."); + return await yt.Videos.Streams.GetAsync(streamInfo); + } + + async Task GetPlayableMediaFromInfoAsync(MediaInfo info) { if (await cache.TryGetAsync(info.Id) is var cachedMedia && cachedMedia is not null) { @@ -155,9 +160,7 @@ static async Task GetPlayableMediaFromInfoAsync(MediaInfo i Stream stream; try { - var manifest = await yt.Videos.Streams.GetManifestAsync(info.Id); - var streamInfo = manifest.GetAudioOnlyStreams().GetWithHighestBitrate() ?? throw new NullReferenceException("Could not get stream from YouTube."); - stream = await yt.Videos.Streams.GetAsync(streamInfo); + stream = await GetMediaHttpStreamAsync(info); } catch (Exception ex) when (IsUnavailable(ex)) { diff --git a/Melodica/Services/Playback/Jukebox.cs b/Melodica/Services/Playback/Jukebox.cs index 54945b2..27c8de3 100644 --- a/Melodica/Services/Playback/Jukebox.cs +++ b/Melodica/Services/Playback/Jukebox.cs @@ -125,17 +125,12 @@ await mediaProcessor.ProcessMediaAsync( token ); } - catch (Exception ex) when (ex is not OperationCanceledException) - { - Log.Error(ex, $"Got exception when trying to write audio:\n{ex.Message}"); - } - finally - { - Log.Debug("Finished sending data... Flushing..."); - await output.WriteSilentFramesAsync(); - await output.FlushAsync(token); - durationTimer.Reset(); - } + catch (OperationCanceledException) { } + + Log.Debug("Finished sending data... Flushing..."); + await output.WriteSilentFramesAsync(); + await output.FlushAsync(token); + durationTimer.Reset(); } async Task DisconnectAsync() diff --git a/Melodica/Services/Playback/JukeboxCommands.cs b/Melodica/Services/Playback/JukeboxCommands.cs index a86b00d..bcd5e14 100644 --- a/Melodica/Services/Playback/JukeboxCommands.cs +++ b/Melodica/Services/Playback/JukeboxCommands.cs @@ -129,7 +129,7 @@ public async Task Queue() [SlashCommand("next", "Sets the next song to play.")] public async Task Next(string query) { - if(!Jukebox.Playing) + if (!Jukebox.Playing) { await RespondAsync("No song is playing! Did you mean to use /play ?", ephemeral: true); return; @@ -210,30 +210,25 @@ public async Task Play(string query, ManualProviderOptions? provider = null) return; } - if (Jukebox.Playing) + + if (Jukebox.Playing) await DeferAsync(true); + else await DeferAsync(); + + try { - try + JukeboxInterface player = new(Context.Interaction); + var result = await Jukebox.PlayAsync(request, voice, player); + if (result == Jukebox.PlayResult.Queued) { var info = await request.GetInfoAsync(); var embed = EmbedUtils.CreateMediaEmbed(info, null); - await RespondAsync("The following media will be queued:", embed: embed, ephemeral: true); - } - catch (Exception ex) - { - await RespondAsync($"Error occured getting media info: {ex}"); - return; + await ModifyOriginalResponseAsync(x => + { + x.Content = "The following media was queued:"; + x.Embed = embed; + }); } } - else - { - await DeferAsync(); // Command can take a long time. - } - - try - { - JukeboxInterface player = new(Context.Interaction); - await Jukebox.PlayAsync(request, voice, player); - } catch (EmptyChannelException) { await ModifyOriginalResponseAsync(x => x.Content = "All users have left the channel. Disconnecting...");