From 64bbf0598eea42e70f88319df5526fd4ac4adcce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=BCller?= Date: Mon, 21 Mar 2022 22:29:59 +0100 Subject: [PATCH] Add Spotify request Caching and more debugging --- TomatenMusicCore/Commands/PlayCommandGroup.cs | 68 ++++++++++++++----- .../Music/Entitites/FullTrackContext.cs | 12 ++-- TomatenMusicCore/Services/SpotifyService.cs | 58 ++++++++++------ 3 files changed, 99 insertions(+), 39 deletions(-) diff --git a/TomatenMusicCore/Commands/PlayCommandGroup.cs b/TomatenMusicCore/Commands/PlayCommandGroup.cs index bf8799f..0d89f20 100644 --- a/TomatenMusicCore/Commands/PlayCommandGroup.cs +++ b/TomatenMusicCore/Commands/PlayCommandGroup.cs @@ -5,6 +5,7 @@ using Lavalink4NET.Player; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using System.Threading.Tasks; using TomatenMusic.Commands.Checks; @@ -35,6 +36,8 @@ namespace TomatenMusic.Commands [OnlyGuildCheck] public async Task PlayQueryCommand(InteractionContext ctx, [Option("query", "The song search query.")] string query) { + var sw = Stopwatch.StartNew(); + await ctx.DeferAsync(true); GuildPlayer player = (GuildPlayer)_audioService.GetPlayer(ctx.Guild.Id); @@ -50,7 +53,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while resolving your query: ``{ex.Message}``, ```{ex.StackTrace}```") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } try @@ -66,7 +71,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while connecting to your Channel: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } } @@ -97,9 +104,13 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while playing your Query: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } - } + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + } [SlashCommand("file", "Play a song file. (mp3/mp4)")] [UserInVoiceChannelCheck] @@ -107,8 +118,9 @@ namespace TomatenMusic.Commands [OnlyGuildCheck] public async Task PlayFileCommand(InteractionContext ctx, [Option("File", "The File that should be played.")] DiscordAttachment file) { + var sw = Stopwatch.StartNew(); - await ctx.DeferAsync(true); + await ctx.DeferAsync(true); GuildPlayer player = (GuildPlayer)_audioService.GetPlayer(ctx.Guild.Id); @@ -123,7 +135,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while resolving your file: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } try @@ -138,7 +152,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while connecting to your Channel: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } } @@ -148,7 +164,9 @@ namespace TomatenMusic.Commands .AddEmbed(Common.AsEmbed(track, player.PlayerQueue.LoopType, 0))); await player.PlayNowAsync(response.Track); - } + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + } } [SlashCommandGroup("play", "Queues or plays the Song")] @@ -172,7 +190,9 @@ namespace TomatenMusic.Commands [OnlyGuildCheck] public async Task PlayQueryCommand(InteractionContext ctx, [Option("query", "The song search query.")] string query) { - await ctx.DeferAsync(true); + var sw = Stopwatch.StartNew(); + + await ctx.DeferAsync(true); GuildPlayer player = (GuildPlayer)_audioService.GetPlayer(ctx.Guild.Id); @@ -187,7 +207,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while resolving your query: ``{ex.Message}``, ```{ex.StackTrace}```") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } try @@ -202,7 +224,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while connecting to your Channel: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } } @@ -233,9 +257,13 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while playing your Track: ``{ex.Message}``, ```{ex.StackTrace}```") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } - } + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + } [SlashCommand("file", "Play a song file. (mp3/mp4)")] [UserInVoiceChannelCheck] @@ -243,8 +271,9 @@ namespace TomatenMusic.Commands [OnlyGuildCheck] public async Task PlayFileCommand(InteractionContext ctx, [Option("File", "The File that should be played.")] DiscordAttachment file) { + var sw = Stopwatch.StartNew(); - await ctx.DeferAsync(true); + await ctx.DeferAsync(true); GuildPlayer player = (GuildPlayer)_audioService.GetPlayer(ctx.Guild.Id); @@ -259,7 +288,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while resolving your file: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } try @@ -274,7 +305,9 @@ namespace TomatenMusic.Commands await ctx.EditResponseAsync(new DiscordWebhookBuilder() .WithContent($"❌ An error occured while connecting to your Channel: ``{ex.Message}``") ); - return; + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); + return; } } @@ -284,6 +317,9 @@ namespace TomatenMusic.Commands .AddEmbed(Common.AsEmbed(track, player.PlayerQueue.LoopType, player.State == PlayerState.NotPlaying ? 0 : player.PlayerQueue.Queue.Count + 1))); await player.PlayAsync(response.Track); + + sw.Stop(); + _logger.LogDebug($"Command {ctx.CommandName} took {sw.ElapsedMilliseconds}ms to execute."); } } } diff --git a/TomatenMusicCore/Music/Entitites/FullTrackContext.cs b/TomatenMusicCore/Music/Entitites/FullTrackContext.cs index df899c7..9fb0091 100644 --- a/TomatenMusicCore/Music/Entitites/FullTrackContext.cs +++ b/TomatenMusicCore/Music/Entitites/FullTrackContext.cs @@ -34,7 +34,7 @@ namespace TomatenMusic.Music.Entitites public int SpotifyPopularity { get; set; } public Uri SpotifyUri { get; set; } - public static async Task PopulateAsync(LavalinkTrack track, string spotifyIdentifier = null) + public static async Task PopulateAsync(LavalinkTrack track, FullTrack spotifyTrack = null, string spotifyId = null) { FullTrackContext context = (FullTrackContext)track.Context; @@ -43,11 +43,15 @@ namespace TomatenMusic.Music.Entitites var spotifyService = TomatenMusicBot.ServiceProvider.GetRequiredService(); var youtubeService = TomatenMusicBot.ServiceProvider.GetRequiredService(); - context.SpotifyIdentifier = spotifyIdentifier; - context.YoutubeUri = new Uri($"https://youtu.be/{track.TrackIdentifier}"); + if (spotifyId != null) + context.SpotifyIdentifier = spotifyId; + else if (spotifyTrack != null) + context.SpotifyIdentifier = spotifyTrack.Id; + + context.YoutubeUri = new Uri(track.Source); track.Context = context; await youtubeService.PopulateTrackInfoAsync(track); - await spotifyService.PopulateTrackAsync(track); + await spotifyService.PopulateTrackAsync(track, spotifyTrack); return track; } diff --git a/TomatenMusicCore/Services/SpotifyService.cs b/TomatenMusicCore/Services/SpotifyService.cs index fe36a3e..0675400 100644 --- a/TomatenMusicCore/Services/SpotifyService.cs +++ b/TomatenMusicCore/Services/SpotifyService.cs @@ -11,6 +11,7 @@ using TomatenMusic.Services; using TomatenMusic.Music; using Lavalink4NET; using Lavalink4NET.Player; +using System.Runtime.Caching; namespace TomatenMusic.Services { @@ -18,9 +19,9 @@ namespace TomatenMusic.Services public interface ISpotifyService { public Task ConvertURL(string url); - public Task PopulateSpotifyPlaylistAsync(SpotifyPlaylist playlist); - public Task PopulateSpotifyAlbumAsync(SpotifyPlaylist playlist); - public Task PopulateTrackAsync(LavalinkTrack track); + public Task PopulateSpotifyPlaylistAsync(SpotifyPlaylist playlist, FullPlaylist spotifyPlaylist = null); + public Task PopulateSpotifyAlbumAsync(SpotifyPlaylist playlist, FullAlbum spotifyAlbum = null); + public Task PopulateTrackAsync(LavalinkTrack track, FullTrack spotifyFullTrack = null); } @@ -29,10 +30,13 @@ namespace TomatenMusic.Services public ILogger _logger { get; set; } public IAudioService _audioService { get; set; } + public ObjectCache Cache { get; set; } + public SpotifyService(SpotifyClientConfig config, ILogger logger, IAudioService audioService) : base(config) { _logger = logger; _audioService = audioService; + Cache = MemoryCache.Default; } public async Task ConvertURL(string url) @@ -43,9 +47,11 @@ namespace TomatenMusic.Services .Replace("https://open.spotify.com/playlist/", "") .Substring(0, 22); + _logger.LogDebug($"Starting spotify conversion for: {url}"); + if (url.StartsWith("https://open.spotify.com/track")) { - FullTrack sTrack = await Tracks.Get(trackId); + FullTrack sTrack = Cache.Contains(trackId) ? Cache.Get(trackId) as FullTrack : await Tracks.Get(trackId); _logger.LogInformation($"Searching youtube from spotify with query: {sTrack.Name} {String.Join(" ", sTrack.Artists)}"); @@ -53,39 +59,43 @@ namespace TomatenMusic.Services if (track == null) throw new ArgumentException("This Spotify Track was not found on Youtube"); - return new MusicActionResponse(await FullTrackContext.PopulateAsync(track, trackId)); + Cache.Add(trackId, sTrack, DateTimeOffset.MaxValue); + + return new MusicActionResponse(await FullTrackContext.PopulateAsync(track, sTrack)); } else if (url.StartsWith("https://open.spotify.com/album")) { List tracks = new List(); - FullAlbum album = await Albums.Get(trackId); + FullAlbum album = Cache.Contains(trackId) ? Cache.Get(trackId) as FullAlbum : await Albums.Get(trackId); foreach (var sTrack in await PaginateAll(album.Tracks)) { _logger.LogInformation($"Searching youtube from spotify with query: {sTrack.Name} {String.Join(" ", sTrack.Artists.ConvertAll(artist => artist.Name))}"); - var track = await _audioService.GetTrackAsync($"{sTrack.Name} {String.Join(" ", sTrack.Artists.ConvertAll(artist => artist.Name))}", Lavalink4NET.Rest.SearchMode.YouTube); if (track == null) throw new ArgumentException("This Spotify Track was not found on Youtube"); - - tracks.Add(await FullTrackContext.PopulateAsync(track, sTrack.Uri.Replace("spotify:track:", ""))); + + tracks.Add(await FullTrackContext.PopulateAsync(track, spotifyId: sTrack.Uri.Replace("spotify:track:", ""))); } Uri uri; Uri.TryCreate(url, UriKind.Absolute, out uri); SpotifyPlaylist playlist = new SpotifyPlaylist(album.Name, album.Id, tracks, uri); - await PopulateSpotifyAlbumAsync(playlist); + await PopulateSpotifyAlbumAsync(playlist, album); + + Cache.Add(trackId, album, DateTimeOffset.MaxValue); return new MusicActionResponse(playlist: playlist); } else if (url.StartsWith("https://open.spotify.com/playlist")) { + List tracks = new List(); - FullPlaylist spotifyPlaylist = await Playlists.Get(trackId); + FullPlaylist spotifyPlaylist = Cache.Contains(trackId) ? Cache.Get(trackId) as FullPlaylist : await Playlists.Get(trackId); foreach (var sTrack in await PaginateAll(spotifyPlaylist.Tracks)) { @@ -98,23 +108,28 @@ namespace TomatenMusic.Services if (track == null) throw new ArgumentException("This Spotify Track was not found on Youtube"); - tracks.Add(await FullTrackContext.PopulateAsync(track, fullTrack.Uri.Replace("spotify:track:", ""))); + tracks.Add(await FullTrackContext.PopulateAsync(track, fullTrack)); } } Uri uri; Uri.TryCreate(url, UriKind.Absolute, out uri); SpotifyPlaylist playlist = new SpotifyPlaylist(spotifyPlaylist.Name, spotifyPlaylist.Id, tracks, uri); - await PopulateSpotifyPlaylistAsync(playlist); + await PopulateSpotifyPlaylistAsync(playlist, spotifyPlaylist); + + Cache.Add(trackId, spotifyPlaylist, DateTimeOffset.MaxValue); return new MusicActionResponse(playlist: playlist); } return null; } - public async Task PopulateSpotifyPlaylistAsync(SpotifyPlaylist playlist) + public async Task PopulateSpotifyPlaylistAsync(SpotifyPlaylist playlist, FullPlaylist spotifyPlaylist = null) { - var list = await this.Playlists.Get(playlist.Identifier); + FullPlaylist list = spotifyPlaylist; + if (list == null) + list = await this.Playlists.Get(playlist.Identifier); + string desc = list.Description; playlist.Description = desc.Substring(0, Math.Min(desc.Length, 1024)) + (desc.Length > 1020 ? "..." : " "); @@ -134,9 +149,12 @@ namespace TomatenMusic.Services return playlist; } - public async Task PopulateSpotifyAlbumAsync(SpotifyPlaylist playlist) + public async Task PopulateSpotifyAlbumAsync(SpotifyPlaylist playlist, FullAlbum spotifyAlbum = null) { - var list = await this.Albums.Get(playlist.Identifier); + FullAlbum list = spotifyAlbum; + if (list == null) + list = await this.Albums.Get(playlist.Identifier); + string desc = list.Label; playlist.Description = desc.Substring(0, Math.Min(desc.Length, 1024)) + (desc.Length > 1020 ? "..." : " "); @@ -148,13 +166,15 @@ namespace TomatenMusic.Services return playlist; } - public async Task PopulateTrackAsync(LavalinkTrack track) + public async Task PopulateTrackAsync(LavalinkTrack track, FullTrack spotifyFullTrack) { FullTrackContext context = (FullTrackContext)track.Context; if (context.SpotifyIdentifier == null) return track; - var spotifyTrack = await this.Tracks.Get(context.SpotifyIdentifier); + FullTrack spotifyTrack = spotifyFullTrack; + if (spotifyTrack == null) + spotifyTrack = await Tracks.Get(context.SpotifyIdentifier); context.SpotifyAlbum = spotifyTrack.Album; context.SpotifyArtists = spotifyTrack.Artists;