I’m working on creating a Discord bot that can handle music playback. Right now my bot can search YouTube and play single tracks, but I’m struggling with implementing a proper queue system.
The main problem is that I can’t figure out how to automatically start the next song when the current one finishes. I tried using the speaking event but it doesn’t work properly since the voice channel connection changes.
const YOUTUBE_API = "<api_key>";
var Discord = require("discord.js");
var ytDownload = require('ytdl-core');
var httpRequest = require('superagent');
var client = new Discord.Client();
var audioConnection = null;
var musicPlaylist = [];
client.on('ready', function() {
console.log('Bot is online');
});
client.on('message', function(msg) {
var cmdParts = msg.content.split(' ');
var mainCommand = cmdParts[0].toLowerCase();
var args = cmdParts.slice(1);
switch (mainCommand) {
case "hello":
msg.reply("Hi there!");
break;
case "!connect":
ConnectToChannel(args[0], msg);
break;
case "!music":
AddToPlaylist(args.join(" "), msg);
break;
}
});
function ConnectToChannel(channelName) {
if (audioConnection) {
audioConnection.disconnect();
}
var targetChannel = FindChannelByName(channelName);
return targetChannel.join();
}
function AddToPlaylist(searchQuery) {
SearchYouTube(searchQuery);
}
function FindChannelByName(name) {
return client.channels.find(ch => ch.name === name);
}
function SearchYouTube(keywords) {
var apiUrl = 'https://www.googleapis.com/youtube/v3/search' + `?part=snippet&q=${escape(keywords)}&key=${YOUTUBE_API}`;
httpRequest(apiUrl, (err, res) => {
if (!err && res.statusCode == 200) {
var data = res.body;
for (var result of data.items) {
if (result.id.kind === 'youtube#video') {
AddToQueue(result.id.videoId);
break;
}
}
}
});
}
function AddToQueue(videoId) {
var videoUrl = `https://www.youtube.com/watch?v=${videoId}`;
musicPlaylist.push(videoUrl);
if (musicPlaylist.length === 1) {
StartPlayback(videoUrl);
}
}
function StartPlayback(url) {
const playOptions = {seek: 0, volume: 0.8};
if (url) {
const audioStream = ytDownload(url, {filter: 'audioonly'});
const player = client.voiceConnections.first().playStream(audioStream, playOptions);
player.on('end', () => {
musicPlaylist.shift();
if (musicPlaylist.length > 0) {
StartPlayback(musicPlaylist[0]);
}
});
}
}
client.login("<token_here>");
What’s the best way to detect when a song ends and automatically play the next one in the queue? Should I be using a different event or approach?