Discord.js voice, player on finish event

Viewed 1483

I'm trying to learn discord.js so I wanted to learn how music bots work. The code below is my shot and everything works but I'm having an issue with my queue.

I cannot get the player.on "finish" or "end" events. So when the song ends I want to call the recursive method that plays a song but I don't know how to get the correct event.

I have tried using player.on(AudioPlayerStatus.Idle) events and making something out of that but that didn't work out so well.

If anyone has any advice to what am I doing wrong or how to improve the code below, I'd be grateful.

const queue = new Map();
module.exports = {
    name: "play",
    aliases: ["skip", "stop", "leave"],
    async execute(message, args, cmd, client, Discord) {

        const voiceChannel = message.member.voice.channel;
        if (!voiceChannel) {
            return message.channel.send({
                content: "You need to be in a channel to execute this command!",
            });
        }

        const permissions = voiceChannel.permissionsFor(message.member);
        if (!(permissions.has('CONNECT')) || !(permissions.has('SPEAK'))) {
            return message.channel.send({
                content: "You don't have the correct permissions!",
            });
        }

        const serverQueue = queue.get(message.guild.id);

        if (cmd == "play") {
            if (!(args.length)) {
                return message.channel.send({
                    content: "Invalid song link!",
                });
            }

            let song = {};

            if (ytdl.validateURL(args[0])) {
                const songInfo = await ytdl.getInfo(args[0]);
                song = { title: songInfo.videoDetails.title, url: songInfo.videoDetails.video_url }
            } else {
                const videoFinder = async (query) => {
                    const videoResult = await ytSearch(query);
                    return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
                }

                const video = await videoFinder(args.join(" "));
                if (video) {
                    song = { title: video.title, url: video.url }
                } else {
                    message.channel.send({
                        content: `No video results found!`
                    })
                }
            }

            if (!(serverQueue)) {
                const queueConstructor = {
                    voiceChannel: voiceChannel,
                    textChannel: message.channel,
                    connection: null,
                    player: null,
                    songs: [],
                }

                queue.set(message.guild.id, queueConstructor);
                queueConstructor.songs.push(song);

                try {
                    const connection = await joinVoiceChannel({
                        channelId: message.member.voice.channel.id,
                        guildId: message.member.voice.channel.guild.id,
                        adapterCreator: message.member.voice.channel.guild.voiceAdapterCreator,
                    });
                    queueConstructor.connection = connection;

                    const player = createAudioPlayer({
                        behaviors: {
                            noSubscriber: NoSubscriberBehavior.Pause
                        }
                    });
                    queueConstructor.player = player;

                    videoPlayer(message.guild, queueConstructor.songs[0]);
                } catch (error) {
                    queue.delete(message.guild.id);

                    message.channel.send({
                        content: `There was an error connecting!`
                    })

                    throw error;
                }
            } else {
                serverQueue.songs.push(song);

                console.log(serverQueue.songs);

                return message.channel.send({
                    content: `${song.title} added to the queue!`
                })
            }
        } else if (cmd == "skip") {
            // TODO
        } else if (cmd == "leave") {
            // TODO
        }

    }

}

const videoPlayer = async (guild, song) => {
    const songQueue = queue.get(guild.id);

    if (!(song)) {
        songQueue.player.stop();
        songQueue.connection.destroy();
        queue.delete(guild.id);
        return;
    }

    const stream = await ytdl(song.url, { filter: "audioonly" });
    const songStream = await (createAudioResource(stream));

    const subscription = songQueue.connection.subscribe(songQueue.player);
    const dispatcher = songQueue.player.play(createAudioResource(stream)).on("finish", () => {
        songQueue.songs.shift();
        songQueue.playing = false;
        videoPlayer(guild, songQueue.songs[0]);
        songQueue.textChannel.send({
            content: `Now playing: ${song.title}`
        });
    });
}
1 Answers
player.addListener("stateChange", (oldOne, newOne) => {
    if (newOne.status == "idle") {
        console.log("The song finished");
    }
});

Use event stateChange. It called when voice state change from oldOne to newOne. (ex: playing -> idle)
newOne { status: "playing | idle | buffering | ..." }

Related