public async Task <KeyValuePair <LavaTrack, bool> > PlayAsync(string query, SocketGuild guild, string source = "youtube", SocketVoiceChannel voiceChannel = null, ITextChannel textChannel = null) { if (voiceChannel != null && textChannel != null && _lavaNode.TryGetPlayer(guild, out var player) == false) { ConnectAsync(voiceChannel, textChannel).Wait(); } player = _lavaNode.GetPlayer(guild); SearchResponse res; switch (source) { case "youtube": res = await _lavaNode.SearchYouTubeAsync(query); break; case "soundcloud": res = await _lavaNode.SearchSoundCloudAsync(query); break; default: res = await _lavaNode.SearchYouTubeAsync(query); break; } var track = res.Tracks.FirstOrDefault(); if (track?.Title == "Track empty" || track is null) { return(new KeyValuePair <LavaTrack, bool>(null, false)); } if (player.PlayerState == PlayerState.Playing || player.PlayerState == PlayerState.Paused) { player.Queue.Enqueue(track); return(new KeyValuePair <LavaTrack, bool>(track, true)); } await player.PlayAsync(track); await player.UpdateVolumeAsync(100); return(new KeyValuePair <LavaTrack, bool>(track, false)); }
/*** * Searches the internet for a song and plays it */ public async Task <Embed> PlayAsync(string query, IGuild guild, SocketGuildUser user) { //Check If User Is Connected To Voice Cahnnel. if (user.VoiceChannel == null) { return(await EmbedHandler.CreateErrorEmbed("Music, Join/Play", "You Must First Join a Voice Channel.")); } //Check the guild has a player available. if (!_lavaNode.HasPlayer(guild)) { return(await EmbedHandler.CreateErrorEmbed("Music, Play", "I'm not connected to a voice channel.")); } try { //Get the player for that guild. var player = _lavaNode.GetPlayer(guild); //Find The Youtube Track the User requested. LavaTrack track; SearchResponse search = new SearchResponse(); if (query.Contains("youtube.com")) { // Search Youtube search = await _lavaNode.SearchYouTubeAsync(query); } else if (query.Contains("soundcloud.com")) { // Search Soundcloud search = await _lavaNode.SearchSoundCloudAsync(query); } else { // Search youtube if not a URL search = await _lavaNode.SearchYouTubeAsync(query); } //If we couldn't find anything, tell the user. if (search.LoadStatus == LoadStatus.NoMatches) { return(await EmbedHandler.CreateErrorEmbed("Music", $"I wasn't able to find anything for {query}.")); } //Get the first track from the search results. //TODO: Add a 1-5 list for the user to pick from. (Like Fredboat) track = search.Tracks.FirstOrDefault(); //If the Bot is already playing music, or if it is paused but still has music in the queue, Add the requested track to the queue. if (player.Track != null && player.PlayerState is PlayerState.Playing || player.PlayerState is PlayerState.Paused) { player.Queue.Enqueue(track); Log.Information("Music", $"{track.Title} has been added to the queue."); return(await EmbedHandler.CreateSongQueueEmbed(track)); } //Player was not playing anything, so lets play the requested track. await player.PlayAsync(track); Log.Information("Music", $"Bot Now Playing: {track.Title}\nUrl: {track.Url}"); return(await EmbedHandler.CreateSongPlayEmbed(track)); } //If after all the checks we did, something still goes wrong. Tell the user about it so they can report it back to us. catch (Exception ex) { return(await EmbedHandler.CreateErrorEmbed("Music, Play", ex.Message)); } }
public async Task SoundCloudSearchAsync([Remainder] string searchQuery) { Language lang = Commands.GetLanguage(Context.Guild.Id); if (!_lavaNode.HasPlayer(Context.Guild)) { IVoiceState voiceState = Context.User as IVoiceState; if (voiceState?.VoiceChannel == null) { EmbedBuilder b = new EmbedBuilder(); b.WithTitle(lang.Not_connected); b.WithFooter(lang.Requested_by + Context.User.Username, Commands.GetUserAvatarUrl(Context.User)); await ReplyAsync(embed : b.Build()); return; } try { await JoinAsync(); } catch (Exception exception) { await Exeptionhandle(exception, "join in play").ConfigureAwait(false); throw; } } LavaPlayer player = _lavaNode.GetPlayer(Context.Guild); if (player.PlayerState == PlayerState.Paused && string.IsNullOrEmpty(searchQuery)) { try { await player.ResumeAsync(); await ReplyAsync(lang.Resumed_front + player.Track.Title); return; } catch (Exception exception) { await Exeptionhandle(exception, "resume in play").ConfigureAwait(false); throw; } } else { if (string.IsNullOrWhiteSpace(searchQuery)) { await ReplyAsync(lang.No_search_term); return; } } Victoria.Responses.Rest.SearchResponse searchResponse = await _lavaNode.SearchSoundCloudAsync(searchQuery); if (searchResponse.LoadStatus == LoadStatus.LoadFailed || searchResponse.LoadStatus == LoadStatus.NoMatches) { EmbedBuilder eb = new EmbedBuilder(); eb.WithTitle(lang.No_results_front + searchQuery + lang.No_results_back); eb.WithFooter(lang.Requested_by + Context.User.Username, Commands.GetUserAvatarUrl(Context.User)); await ReplyAsync($"", false, eb.Build()); return; } if (player.PlayerState == PlayerState.Playing || player.PlayerState == PlayerState.Paused) { if (!string.IsNullOrWhiteSpace(searchResponse.Playlist.Name)) { foreach (LavaTrack track in searchResponse.Tracks) { player.Queue.Enqueue(track); } await ReplyAsync($"Enqueued {searchResponse.Tracks.Count} tracks."); } else { LavaTrack track = searchResponse.Tracks[0]; player.Queue.Enqueue(track); await ReplyAsync($"Enqueued: {track.Title}"); } } else { LavaTrack track = searchResponse.Tracks[0]; if (!string.IsNullOrWhiteSpace(searchResponse.Playlist.Name)) { for (int i = 0; i < searchResponse.Tracks.Count; i++) { if (i == 0) { await player.PlayAsync(track); EmbedBuilder b = new EmbedBuilder(); b.WithTitle($"Now playing: {track.Title}"); b.AddField("source", "<:soundcloud:793405493204090911> search"); string art = await track.FetchArtworkAsync(); b.WithThumbnailUrl(art); b.WithFooter(lang.Requested_by + Context.User.Username, Commands.GetUserAvatarUrl(Context.User)); await ReplyAsync(embed : b.Build()); } else { if (searchResponse.Tracks[i] == null) { await ReplyAsync(i + " was null"); } player.Queue.Enqueue(searchResponse.Tracks[i]); } } await ReplyAsync($"Enqueued {searchResponse.Tracks.Count} tracks."); } else { await player.PlayAsync(track); EmbedBuilder b = new EmbedBuilder(); b.WithTitle($"Now playing: {track.Title}"); b.AddField("source", "<:soundcloud:793405493204090911> search"); string art = await track.FetchArtworkAsync(); b.WithThumbnailUrl(art); b.WithFooter(new Language().Requested_by + Context.User.Username, Commands.GetUserAvatarUrl(Context.User)); await ReplyAsync(embed : b.Build()); } } }
public async Task <LavaTrack?> PlayTrackAsync(string song, ICommandContext context, ITextChannel textChannel, bool useSC) { bool isLink = song.StartsWith("https://youtube.com/watch?v=") || song.StartsWith("youtube.com/watch?v=") || song.StartsWith("soundcloud.com") || song.StartsWith("https://soundcloud.com") || song.StartsWith("https://www.youtube.com/playlist?list=") || song.StartsWith("youtube.com/playlist?list="); var usersVc = context.UsersVC() as SocketVoiceChannel; var user = context.User as IGuildUser; var player = await JoinAsync(context, textChannel, true, false); var guild = GetOrAddConfig(context.Guild.Id); if (player == null) { return(null); } if (usersVc.Users.Count > 2 && !user.HasRole("DJ") && player.PlayerState == PlayerState.Playing) { return(await QueueAsync(song, context, textChannel, useSC)); } SearchResponse response; if (!isLink) { if (useSC) { response = await _audio.SearchSoundCloudAsync(song); } else { response = await _audio.SearchYouTubeAsync(song); } } else { response = await _audio.SearchAsync(song); } if (response.Tracks.Count == 0) { await textChannel.SendMessageAsync(":musical_note: Cannot find the song :(."); return(null); } if (response.Tracks.Count > 1 && (isLink && song.Contains("playlist?list="))) { var tracks = new List <TrackInput>(); foreach (var item in response.Tracks) { tracks.Add(new TrackInput(item, context.User.Id)); } for (int i = 1; i < tracks.Count; i++) { guild.Queue.Enqueue(tracks[i]); } } var track = response.Tracks.FirstOrDefault(); await player.PlayAsync(track); guild.CurrentUserPlaying = context.User.Id; { EmbedBuilder Embed = new EmbedBuilder(); Embed.Title = "Playing Song"; Embed.Description = $"[{track.Title}]({track.Url})"; Embed.AddField("Length", track.Duration.ToString(), true); if (guild.Repeat) { Embed.AddField("Tracks in Queue", (guild.Queue.Count).ToString(), true); } else { Embed.AddField("Tracks in Queue", (guild.Queue.Count).ToString(), true); } Embed.AddField("Next Track", guild.Queue.Count == 0 ? "No tracks" : (guild.Queue[0].Track as LavaTrack).Title, true); Embed.ImageUrl = await track.FetchArtworkAsync(); await textChannel.SendMessageAsync("", false, Embed.Build()); } return(track); }
public async Task PlayAsync([Remainder] string query = null) { if (!_lavaNode.HasPlayer(Context.Guild)) { await ReplyAndDeleteAsync(":x: I am not connected to a voice channel."); return; } var player = _lavaNode.GetPlayer(Context.Guild); if (query == null && player.Queue.TryDequeue(out var queueable)) { if (!(queueable is LavaTrack track)) { await ReplyAndDeleteAsync(":x: Next item in queue is not a track."); return; } await player.PlayAsync(track); } var isUri = Uri.TryCreate(query, UriKind.Absolute, out _); var searchResponse = isUri ? await _lavaNode.SearchAsync(query) : await _lavaNode.SearchYouTubeAsync(query); if (!(searchResponse.LoadStatus != LoadStatus.LoadFailed && searchResponse.LoadStatus != LoadStatus.NoMatches) && !isUri) { searchResponse = await _lavaNode.SearchSoundCloudAsync(query); } if (searchResponse.LoadStatus == LoadStatus.LoadFailed || searchResponse.LoadStatus == LoadStatus.NoMatches) { await ReplyAndDeleteAsync($":x: No search results for `{query}`."); return; } var tracks = searchResponse.Tracks; if (player.PlayerState == PlayerState.Playing || player.PlayerState == PlayerState.Paused) { if (!string.IsNullOrWhiteSpace(searchResponse.Playlist.Name)) { player.Queue.Enqueue(tracks); await ReplyAndDeleteAsync($"Enqueued {tracks.Count} tracks."); return; } else { var eta = ""; var track = tracks.First(); player.Queue.Enqueue(track); var artwork = await track.FetchArtworkAsync(); var embed = new EmbedBuilder() .WithTitle("Enqueued") .WithDescription($"[{track.Title}]({track.Url})") .WithThumbnailUrl(artwork) .AddField("Duration", $"`{track.Duration.StripMilliseconds()}`", true) .AddField("ETA", $"`{eta}`"); await ReplyAndDeleteAsync(embed : embed.Build()); return; } } else { await player.PlayAsync(tracks.First()); if (!string.IsNullOrWhiteSpace(searchResponse.Playlist.Name)) { player.Queue.Enqueue(tracks.Skip(1)); await ReplyAndDeleteAsync($"Enqueued {tracks.Count - 1} tracks."); return; } await Context.Message.DeleteAsync(); } }
/// <summary> /// Searches the specified <see cref="SearchProvider" /> for the provided <see cref="query" />. /// This method also adds the song to the guild's player queue and will even join the user's voice /// channel automatically. /// </summary> /// <param name="context"></param> /// <param name="query">The song to search for, user input.</param> /// <param name="playFirst"></param> /// <param name="provider"></param> /// <returns></returns> public async Task <ReactionCallbackData> SearchAndPlayAsync(ShardedCommandContext context, string query, bool playFirst = false, SearchProvider provider = SearchProvider.YOU_TUBE) { User user = await DatabaseQueries.GetOrCreateUserAsync(context.User.Id); Server server = await DatabaseQueries.GetOrCreateServerAsync(context.Guild.Id); LavaNode node = ConfigProperties.LavaNode; SocketVoiceChannel curVc = (context.User as SocketGuildUser).VoiceChannel; await ConsoleLogger.LogAsync($"Found node and voice channel for guild {context.Guild.Id}.", LogLvl.TRACE); if (curVc == null) { await context.Channel.SendMessageAsync($"{context.User.Mention} You must be in a voice " + "channel to use this command."); await ConsoleLogger.LogAsync("User was not in voice channel, cancelling music search operation.", LogLvl.TRACE); return(null); } SearchResponse result = provider switch { SearchProvider.YOU_TUBE => await node.SearchYouTubeAsync(query), SearchProvider.SOUNDCLOUD => await node.SearchSoundCloudAsync(query), _ => await node.SearchAsync(query) }; if (provider == SearchProvider.TWITCH) { const string PROVIDER_URL = "www.twitch.tv"; string errorString = "Your search returned no results. Ensure you are only " + "typing the name of the streamer who you want to watch or a direct link to their stream.\n\n" + "Note: The streamer must be live for this feature to work."; if (!query.ToLower().Contains(PROVIDER_URL)) { result = await node.SearchAsync($"https://{PROVIDER_URL}/{query}"); if (result.Tracks.Count == 0) { await context.Channel.SendBasicErrorEmbedAsync(errorString); await ConsoleLogger.LogAsync($"No livestream found for search {query} in guild {context.Guild.Id}.", LogLvl.TRACE); return(null); } } else { if ((await node.SearchAsync($"https://{PROVIDER_URL}/{query.Split('\\').Last()}")).Tracks.Count == 0 && (await node.SearchAsync(query)).Tracks.Count == 0) { await context.Channel.SendBasicErrorEmbedAsync(errorString); await ConsoleLogger.LogAsync($"No livestream found for search {query} in guild {context.Guild.Id}.", LogLvl.TRACE); return(null); } } } var tracks = new List <LavaTrack>(); if (user.IsPremium || server.IsPremium) { if (result.Tracks.Any()) { tracks.AddRange(result.Tracks); } } else { // Limit track duration to 10 minutes for non-premium servers/users. if (result.Tracks.Any()) { tracks.AddRange(result.Tracks.Where(x => x.Duration.TotalMinutes < 10).ToList()); } } if (!tracks.Any()) { string suppString = user.IsPremium ? "" : "If you are " + $"not a [Kaguya Premium Subscriber]({ConfigProperties.KAGUYA_STORE_URL}), " + "you are only limited to playing songs less than `10 minutes` in duration."; await context.Channel.SendBasicErrorEmbedAsync($"Your requested search returned no results. {suppString}"); await ConsoleLogger.LogAsync("Search request returned no usable " + $"results in guild {Context.Guild.Id} for query {query}", LogLvl.TRACE); } var fields = new List <EmbedFieldBuilder>(); var callbacks = new List <(IEmote, Func <SocketCommandContext, SocketReaction, Task>)>(); Emoji[] emojiNums = GlobalProperties.EmojisOneThroughNine(); LavaPlayer player = node.HasPlayer(context.Guild) ? node.GetPlayer(context.Guild) : await node.JoinAsync(curVc); await ConsoleLogger.LogAsync($"Player found for guild {context.Guild.Id}. Connected to voice channel.", LogLvl.TRACE); #region If the track is a livestream: if (tracks.Any(x => x.IsStream)) { LavaTrack trackSel = tracks.First(x => x.IsStream); // Gathers the first stream from the collection. string twitchName = (await ConfigProperties.TwitchApi.V5.Users.GetUserByNameAsync(trackSel.Author)).Matches[0].DisplayName; string playString = player.PlayerState == PlayerState.Playing ? $"Queued stream into position {player.Queue.Count}." : $"Now playing `{twitchName}`'s stream."; if (player.PlayerState == PlayerState.Playing) { try { player.Queue.Enqueue(trackSel); await ConsoleLogger.LogAsync($"Enqueued livestream {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to enqueue the livestream " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } else { try { await player.PlayAsync(trackSel); await ConsoleLogger.LogAsync($"Playing livestream {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } var field = new EmbedFieldBuilder { Name = $"`{twitchName}`'s Stream", Value = $"{playString}\n" // We get rid of backticks for formatting. }; var embed = new KaguyaEmbedBuilder { Fields = new List <EmbedFieldBuilder> { field } }; await context.Channel.SendEmbedAsync(embed); return(null); } #endregion #region If we have chosen to only play the default track (via $play). if (playFirst && tracks.Any()) { LavaTrack trackSel = tracks[0]; var field = new EmbedFieldBuilder { Name = "Track #1.", Value = $"Title: `{trackSel.Title.Replace("`", "")}`\n" + // We get rid of backticks for formatting. $"Duration: `{trackSel.Duration.Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 3)}`\n" + $"Uploader: `{trackSel.Author}`" }; string playString = player.PlayerState == PlayerState.Playing && !player.Track.IsStream ? $"Queued track #1 into position {player.Queue.Count + 1}." : "Now playing track #1."; if (player.PlayerState == PlayerState.Playing) { if (player.Queue.Count() == 50 && !server.IsPremium) { await ConsoleLogger.LogAsync($"Queue is full in {context.Guild.Id}, sending error.", LogLvl.TRACE); await SendBasicErrorEmbedAsync("Your queue is full! `50 songs` is the maximum " + $"for non [Kaguya Premium]({ConfigProperties.KAGUYA_STORE_URL}) " + "servers."); } else { player.Queue.Enqueue(trackSel); await ConsoleLogger.LogAsync($"Enqueued track {trackSel.Title} in guild {context.Guild.Id}.", LogLvl.TRACE); if (player.Track.IsStream) { await player.SkipAsync(); await ConsoleLogger.LogAsync($"Skipped livestream to play incoming track in guild {context.Guild.Id}.", LogLvl.TRACE); } } } else { try { await player.PlayAsync(trackSel); await ConsoleLogger.LogAsync($"Playing track {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } if (player.Volume == 0 && player.PlayerState == PlayerState.Playing) { await player.UpdateVolumeAsync(75); // Sets the volume back to default if it is muted. await ConsoleLogger.LogAsync($"Automatically set player volume to 75 in guild {context.Guild.Id}.", LogLvl.TRACE); } var embed = new KaguyaEmbedBuilder { Title = $"Kaguya Music {Centvrio.Emoji.Music.Notes}", Description = playString, ThumbnailUrl = await trackSel.FetchArtworkAsync(), Fields = new List <EmbedFieldBuilder> { field } }; await SendEmbedAsync(embed, context); return(null); } #endregion int h = tracks.Count; for (int i = 0; i < (h < 7 ? h : 7); i++) { int i1 = i; LavaTrack trackSel = tracks[i]; var field = new EmbedFieldBuilder { Name = $"Track {i1 + 1}.", Value = $"Title: `{trackSel.Title.Replace("`", "")}`\n" + // We get rid of backticks for formatting. $"Duration: `{trackSel.Duration.Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 3)}`\n" + $"Uploader: `{trackSel.Author}`" }; fields.Add(field); callbacks.Add((emojiNums[i], async(c, r) => { string playString = player.PlayerState == PlayerState.Playing && !player.Track.IsStream ? $"Queued track #{i1 + 1} into position {player.Queue.Count + 1}." : $"Now playing track #{i1 + 1}."; if (player.PlayerState == PlayerState.Playing) { if (player.Queue.Count() == 50 && !server.IsPremium) { await ConsoleLogger.LogAsync($"Queue was full in guild {context.Guild.Id}. Sending error message.", LogLvl.TRACE); await SendBasicErrorEmbedAsync("Your queue is full! `50 songs` is the maximum " + $"for non [Kaguya Premium]({ConfigProperties.KAGUYA_STORE_URL}) " + "servers."); return; } player.Queue.Enqueue(trackSel); await ConsoleLogger.LogAsync($"Enqueued track {trackSel} in guild {context.Guild.Id}", LogLvl.TRACE); if (player.Track.IsStream) { await player.SkipAsync(); await ConsoleLogger.LogAsync("Automatically skipped livestream to play" + $" incoming track in guild {context.Guild.Id}", LogLvl.TRACE); } } else { try { await player.PlayAsync(trackSel); await ConsoleLogger.LogAsync($"Playing track {trackSel.Title} in guild {context.Guild.Id}", LogLvl.TRACE); } catch (Exception e) { await ConsoleLogger.LogAsync("An exception was thrown when trying to play track " + $"{trackSel.Title} in guild {Context.Guild.Id}.\n" + $"Exception Message: {e.Message}\n" + $"Stack Trace: {e.StackTrace}", LogLvl.WARN); } } if (player.Volume == 0 && player.PlayerState == PlayerState.Playing) { await player.UpdateVolumeAsync(75); // Sets the volume back to default if it is muted. await ConsoleLogger.LogAsync($"Automatically set volume to 75 in guild {context.Guild.Id}", LogLvl.TRACE); } var embed = new KaguyaEmbedBuilder { Title = $"Kaguya Music {Centvrio.Emoji.Music.Notes}", Description = playString, ThumbnailUrl = await trackSel.FetchArtworkAsync(), Fields = new List <EmbedFieldBuilder> { field } }; await SendEmbedAsync(embed); } )); } callbacks.Add((GlobalProperties.NoEntryEmoji(), async(c, r) => { await c.Message.DeleteAsync(); await r.Message.Value.DeleteAsync(); })); string s = tracks.Count == 1 ? "" : "s"; var songDisplayEmbed = new KaguyaEmbedBuilder { Title = "Kaguya Music Search Results", Description = $" I found {tracks.Count} track{s} from {provider}, " + $"{(tracks.Count > 5 ? "but here are the top 5" : "here they are")}. " + "Please select a track to play.", Fields = fields }; var data = new ReactionCallbackData("", songDisplayEmbed.Build(), false, false, TimeSpan.FromSeconds(60)); data.SetCallbacks(callbacks); return(data); } }