/// <summary> /// Searches Spotify for tracks /// </summary> /// <param name="channel">Channel in which to send to</param> /// <param name="url">Spotify URL</param> /// <returns><see cref="List{T}"/> of tracks (track name + artists' name) or null if not found</returns> public async Task <List <string> > SearchSpotify(ISocketMessageChannel channel, string url) { Regex r = new Regex(@"https?:\/\/(?:open\.spotify\.com)\/(?<type>\w+)\/(?<id>[\w-]{22})(?:\?si=(?:[\w-]{22}))?"); if (!r.Match(url).Success) { var msg = await embedHelper.BuildMessageEmbed("Invalid Spotify link."); await channel.SendAndRemove(embed : msg, timeout : 5000); return(null); } string type = r.Match(url).Groups["type"].Value; string id = r.Match(url).Groups["id"].Value; List <string> tracks = new List <string>(); switch (type) { case "album": await foreach (var item in Spotify.Paginate((await Spotify.Albums.Get(id)).Tracks)) { tracks.Add($"{item.Name} {string.Join(" ", item.Artists.Select(x => x.Name))}"); } break; case "playlist": var playlist = await Spotify.Playlists.Get(id); await foreach (var item in Spotify.Paginate(playlist.Tracks)) { if (item.Track is FullTrack track) { tracks.Add($"{track.Name} {string.Join(" ", track.Artists.Select(x => x.Name))}"); } } break; case "track": var trackItem = await Spotify.Tracks.Get(id); tracks.Add($"{trackItem.Name} {string.Join(" ", trackItem.Artists.Select(x => x.Name))}"); break; default: var msg = await embedHelper.BuildMessageEmbed("Must be a `track`, `playlist`, or `album`."); await channel.SendAndRemove(embed : msg, timeout : 6000); return(null); } return(tracks); }
public static async Task ShuffleAsync( this LavaPlayer player, AudioHelper audioHelper, EmbedHelper embedHelper, IMessageChannel channel = null, SocketCommandContext context = null) { if (player.Queue.Count < 2) { return; } player.Queue.Shuffle(); if (context != null && !context.Guild.TextChannels.Where(x => x.Id == Program.BotConfig.ChannelId).Any()) { return; } channel ??= context?.Channel; string newQueue = await AudioHelper.GetNewEmbedQueueString(player); await Program.BotConfig.BotEmbedMessage.ModifyAsync(x => x.Content = string.Format(AudioHelper.QueueMayHaveSongs, newQueue)); if (channel != null) { var msg = await embedHelper.BuildMessageEmbed("Queue shuffled"); await channel.SendAndRemove(embed : msg); } }
public static async Task LoopAsync(this LavaPlayer player, AudioHelper audioHelper, EmbedHelper embedHelper, IMessageChannel channel) { audioHelper.RepeatFlag = !audioHelper.RepeatFlag; audioHelper.RepeatTrack = audioHelper.RepeatFlag switch { true => player.Track, false => null }; var embed = await embedHelper.BuildMessageEmbed($"Loop set to `{(audioHelper.RepeatFlag ? "enabled" : "disabled")}`"); await channel.SendAndRemove(embed : embed, timeout : 5000); }
public AudioHelper(LavaNode lavanode, EmbedHelper eh) { Node = lavanode; embedHelper = eh; // TODO: Make SpotifyClient own class if (Program.BotConfig.SpotifyClientId != "" && Program.BotConfig.SpotifySecret != "") { var config = SpotifyClientConfig.CreateDefault(); var request = new ClientCredentialsRequest(Program.BotConfig.SpotifyClientId, Program.BotConfig.SpotifySecret); var response = new OAuthClient(config).RequestToken(request); var spotify = new SpotifyClient(config.WithToken(response.Result.AccessToken)); Spotify = spotify; _disconnectTokens = new ConcurrentDictionary <ulong, CancellationTokenSource>(); SpotifyLogin = true; } // Handler for when a LavaTrack is started // args: TrackStartEventArgs Node.OnTrackStarted += async(args) => { var player = args.Player; var queue = await GetNewEmbedQueueString(player); var embed = await embedHelper.BuildMusicEmbed(player, Color.DarkTeal); //If for some reason Volume is set to 0 (100%) it will set to default volume if (player.Volume == 0) { await player.UpdateVolumeAsync(Program.BotConfig.Volume); } var content = queue switch { "" => NoSongsInQueue, _ => string.Format(QueueMayHaveSongs, queue) }; await Program.BotConfig.BotEmbedMessage.ModifyAsync(x => { x.Embed = embed; x.Content = content; }); if (!_disconnectTokens.TryGetValue(args.Player.VoiceChannel.Id, out var value)) { return; } if (value.IsCancellationRequested) { return; } value.Cancel(true); }; // Handler for when a LavaTrack is ended, stopped, or skipped // args: TrackEndedEventArgs Node.OnTrackEnded += async(args) => { var player = args.Player; if (RepeatFlag) { await player.PlayAsync(RepeatTrack); return; } RepeatFlag = false; if (!args.Reason.ShouldPlayNext()) { return; } if (!player.Queue.TryDequeue(out var track) && player.Queue.Count == 0) { var embed = await EmbedHelper.BuildDefaultEmbed(); await Program.BotConfig.BotEmbedMessage.ModifyAsync(x => { x.Content = NoSongsInQueue; x.Embed = embed; }); if (!StayFlag) { _ = InitiateDisconnectAsync(args.Player, TimeSpan.FromMinutes(15)); } return; } await args.Player.PlayAsync(track); }; // Handler for when a LavaTrack throws an exception // args: TrackExceptionEventArgs Node.OnTrackException += async(args) => { var player = args.Player; var errorMessage = args.ErrorMessage switch { "This video cannot be viewed anonymously." => "This video most likely hasn't premiered yet.", "Received unexpected response from YouTube." => "YouTube is most likely down.", _ => "Video might still be processing, try again later." }; var msg = await embedHelper.BuildTrackErrorEmbed($"[{player.Track.Title}]({player.Track.Url})\n{errorMessage}"); await player.TextChannel.SendAndRemove(embed : msg); if (player.Queue.Count == 0) { //If no songs in queue it will stop playback to reset the embed await player.StopAsync(); return; } //If queue has any songs it will skip to the next track await player.SkipAsync(); return; }; // Handler for when a LavaTrack gets stuck // args: TrackStuckEventArgs Node.OnTrackStuck += async(args) => { var player = args.Player; var msg = await embedHelper.BuildTrackErrorEmbed($"[{player.Track.Title}]({player.Track.Url})\nTrack got stuck, moving on..."); await player.TextChannel.SendAndRemove(embed : msg); if (player.Queue.Count == 0) { //If no songs in queue it will stop playback to reset the embed await player.StopAsync(); return; } //If queue has any songs it will skip to the next track await player.SkipAsync(); return; }; } /// <summary> /// Initiates a disconnect from the voice channel /// </summary> /// <param name="player">Instance of LavaPlayer for the guild</param> /// <param name="timeSpan">TimeSpan for length before disconnecting</param> /// <returns></returns> private async Task InitiateDisconnectAsync(LavaPlayer player, TimeSpan timeSpan) { if (!_disconnectTokens.TryGetValue(player.VoiceChannel.Id, out var value)) { value = new CancellationTokenSource(); _disconnectTokens.TryAdd(player.VoiceChannel.Id, value); } else if (value.IsCancellationRequested) { _disconnectTokens.TryUpdate(player.VoiceChannel.Id, new CancellationTokenSource(), value); value = _disconnectTokens[player.VoiceChannel.Id]; } var isCancelled = SpinWait.SpinUntil(() => value.IsCancellationRequested, timeSpan); if (isCancelled) { return; } await Node.LeaveAsync(player.VoiceChannel); var msg = await embedHelper.BuildMessageEmbed("Muse has disconnected due to inactivity."); await player.TextChannel.SendAndRemove(embed : msg); }