From 3bdb592671daca548e6d6eb86177f71a987faad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=BCller?= Date: Sun, 20 Mar 2022 18:33:49 +0100 Subject: [PATCH 1/3] change GitVersion.yml --- GitVersion.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index ad1bdae..af41166 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -4,7 +4,7 @@ branches: regex: ^master$|^main$ mode: ContinuousDelivery tag: '' - increment: None + increment: Minor prevent-increment-of-merged-branch-version: true track-merge-target: false source-branches: [ 'develop', 'release' ] @@ -16,7 +16,7 @@ branches: regex: ^dev(elop)?(ment)?$ mode: ContinuousDeployment tag: pre - increment: None + increment: Patch prevent-increment-of-merged-branch-version: false track-merge-target: true source-branches: [] @@ -27,3 +27,7 @@ branches: ignore: sha: [] merge-message-formats: {} +major-version-bump-message: '\+semver:\s?(breaking|major)' +minor-version-bump-message: '\+semver:\s?(feature|minor)' +patch-version-bump-message: '\+semver:\s?(fix|patch)' +commit-message-incrementing: Enabled From 90514e8e624e90509c39be537bc679f24b2b6108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=BCller?= Date: Sun, 20 Mar 2022 18:45:12 +0100 Subject: [PATCH 2/3] Implemented Card https://cloud.tomatentum.net/apps/deck/#/board/19/card/229 --- TomatenMusicCore/Music/GuildPlayer.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/TomatenMusicCore/Music/GuildPlayer.cs b/TomatenMusicCore/Music/GuildPlayer.cs index b592c29..e8c54cf 100644 --- a/TomatenMusicCore/Music/GuildPlayer.cs +++ b/TomatenMusicCore/Music/GuildPlayer.cs @@ -134,7 +134,6 @@ namespace TomatenMusic.Music public async Task PlayPlaylistNowAsync(LavalinkPlaylist playlist) { - EnsureConnected(); EnsureNotDestroyed(); if (!PlayerQueue.Queue.Any()) @@ -159,8 +158,16 @@ namespace TomatenMusic.Music public async Task RewindAsync() { - MusicActionResponse response = PlayerQueue.Rewind(); + EnsureNotDestroyed(); + EnsureConnected(); + if (Position.Position.Seconds < 4) + { + await ReplayAsync(); + return; + } + MusicActionResponse response = PlayerQueue.Rewind(); + _logger.LogInformation($"Rewinded Track {CurrentTrack.Title} for Track {response.Track.Title}"); await base.PlayAsync(response.Track); QueuePrompt.UpdateFor(GuildId); @@ -168,6 +175,8 @@ namespace TomatenMusic.Music public async Task SkipAsync() { + EnsureNotDestroyed(); + EnsureConnected(); MusicActionResponse response = PlayerQueue.NextTrack(true); _logger.LogInformation($"Skipped Track {CurrentTrack.Title} for Track {response.Track.Title}"); 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 3/3] 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;