how to set and track audio player states?

hi there, im learning data structures and algorithms and i thought it would be a nice exercise using them in a discord bot that takes a local folder and creates a track instance and plays it according to an initialized DSA... the thing is, i need to keep track of the player status, but it seems that if (player.state === AudioPlayerStatus.__) have no overlap and it complains, ive seen some player.on(___, async () =>) implementations but looks confusing and verbose, is there any way to properly track and set player states to ensure the behaviour of a queue, stack or whatever?
try {
TrackQueue.enqueue(track);
const currTrack = TrackQueue.peek();

if (typeof currTrack !== "undefined") {
// player.state === 'idle', but it triggers anyway and returns this interaction
if (AudioPlayerStatus.Playing) { // Adds
return await interaction.followUp({
ephemeral: true,
content: `Added to the queue... ***${currTrack.title}***`,
});
}

player.play(currTrack.res);

await interaction.followUp({
ephemeral: true,
content: `Now playing... [${currTrack.title}](${currTrack.path})`,
});

return entersState(player, AudioPlayerStatus.Playing, 5000);
}
} catch (error) {
console.log(error);
return await interaction.followUp({
ephemeral: true,
content:
"Something went wrong playing and maybe I crashed, but I'll be back... I hope so :anguished:",
});
}
try {
TrackQueue.enqueue(track);
const currTrack = TrackQueue.peek();

if (typeof currTrack !== "undefined") {
// player.state === 'idle', but it triggers anyway and returns this interaction
if (AudioPlayerStatus.Playing) { // Adds
return await interaction.followUp({
ephemeral: true,
content: `Added to the queue... ***${currTrack.title}***`,
});
}

player.play(currTrack.res);

await interaction.followUp({
ephemeral: true,
content: `Now playing... [${currTrack.title}](${currTrack.path})`,
});

return entersState(player, AudioPlayerStatus.Playing, 5000);
}
} catch (error) {
console.log(error);
return await interaction.followUp({
ephemeral: true,
content:
"Something went wrong playing and maybe I crashed, but I'll be back... I hope so :anguished:",
});
}
12 Replies
d.js toolkit
d.js toolkit14mo ago
• What's your exact discord.js npm list discord.js and node node -v version? • Post the full error stack trace, not just the top part! • Show your code! • Explain what exactly your issue is. • Not a discord.js issue? Check out #useful-servers.
hapless.dev
hapless.dev14mo ago
"dependencies": {
"@discordjs/core": "^0.6.0",
"@discordjs/opus": "^0.9.0",
"@discordjs/voice": "^0.16.0",
"discord-api-types": "^0.37.43",
"discord.js": "^14.11.0",
"dotenv": "^16.1.4",
"ffmpeg": "^0.0.4",
"libsodium-wrappers": "^0.7.11",
"nodemon": "^2.0.22",
"sodium": "^3.0.2",
}
"dependencies": {
"@discordjs/core": "^0.6.0",
"@discordjs/opus": "^0.9.0",
"@discordjs/voice": "^0.16.0",
"discord-api-types": "^0.37.43",
"discord.js": "^14.11.0",
"dotenv": "^16.1.4",
"ffmpeg": "^0.0.4",
"libsodium-wrappers": "^0.7.11",
"nodemon": "^2.0.22",
"sodium": "^3.0.2",
}
Node 18.16.0 how can i make if statements with player.status if AudioPlayerStatus has no overlap and cant call AudioPlayerState enums since are types and not values?
Kinect3000
Kinect300014mo ago
Use player.state.status
hapless.dev
hapless.dev14mo ago
i figured out that, but now i have the same issue, how can i track the state between resources?
try {
const addedTrack = await Track.create(url);
TrackQueue.enqueue(addedTrack);

while (!TrackQueue.isEmpty()) {
const currTrack = TrackQueue.peek();

if (player.state.status === AudioPlayerStatus.Playing) {
await interaction.followUp({
ephemeral: true,
content: `Added to the queue... **${addedTrack?.title}**<${addedTrack?.path}>`,
});
break;
}

if (player.state.status === AudioPlayerStatus.Idle) {
if (currTrack) {
TrackQueue.dequeue();
player.play(currTrack.res);
await entersState(player, AudioPlayerStatus.Playing, 5000);
await interaction.followUp({
ephemeral: true,
content: `Now playing... **${currTrack.title}**<${currTrack.path}>`,
});
}
}
}
try {
const addedTrack = await Track.create(url);
TrackQueue.enqueue(addedTrack);

while (!TrackQueue.isEmpty()) {
const currTrack = TrackQueue.peek();

if (player.state.status === AudioPlayerStatus.Playing) {
await interaction.followUp({
ephemeral: true,
content: `Added to the queue... **${addedTrack?.title}**<${addedTrack?.path}>`,
});
break;
}

if (player.state.status === AudioPlayerStatus.Idle) {
if (currTrack) {
TrackQueue.dequeue();
player.play(currTrack.res);
await entersState(player, AudioPlayerStatus.Playing, 5000);
await interaction.followUp({
ephemeral: true,
content: `Now playing... **${currTrack.title}**<${currTrack.path}>`,
});
}
}
}
this generates an "infinite" loop where the bot constantly sends follow ups to the original command request but also does the job of keep playing until the queue is empty, how can i either: a. keep track constantly of the state of the player and change it accordingly, or b. send only and only one interaction even on a loop? c all of the above? haha oh, i have to remove it, came from the first blogs i was following that are by now fairly deprecated, my bad thank you for noticing it!
Kinect3000
Kinect300014mo ago
You should be listening to the AudioPlayer.stateChange event instead of the while loop
hapless.dev
hapless.dev14mo ago
how can i do that? ive seen implementations with player.on('stateChange' but couldnt understand how to then swap the already played resource with one that is pending in the queue
Kinect3000
Kinect300014mo ago
Swap? You just play the new resource
hapless.dev
hapless.dev14mo ago
i mean like, continue the queue
Kinect3000
Kinect300014mo ago
Pop the queue, then play the song
hapless.dev
hapless.dev14mo ago
and whats the syntax of the stateChange? is with the player.on or is there another way?
Kinect3000
Kinect300014mo ago
It’s an event that provides the old and new state Normal event emitter syntax
hapless.dev
hapless.dev14mo ago
do you have an example snippet that i can check? when i access AudioPlayer.stateChange the property doesnt exist, and .on of course is available since is the class that creates the player but im farily lost and confused by now, im so sorry for all the hassle 😭 i did it! although its not the most clean code and im sure its reduntand af, but couldnt figure out how to track the state without creating a loop that emptied the queue when changing state or plain out making a queue underflow, thanks for all the patience and sorry if i came out as lazy or frustratingly dumb, i was really tired since it was late here, thanks once again for the guidance this was my implementation
// HACK: Proper refactor, this ugly af, `stateChange` already gives me access
// to previous states, so its redundant, but if not, loops until queue underflow
try {
const addedTrack = await Track.create(path);
TrackQueue.enqueue(addedTrack);
let currTrack = TrackQueue.peek();

if (player.state.status === AudioPlayerStatus.Idle) {
if (currTrack) {
player.play(currTrack.res);

await interaction.followUp({
ephemeral: false,
content: `Now playing... [**${currTrack.title}**](<${currTrack.path}>).`,
});
}
} else {
await interaction.followUp({
ephemeral: true,
content: `Added to queue... [**${addedTrack.title}**](<${addedTrack.path}>).`,
});
}

player.on("stateChange", async (prev) => {
const nextTrack = TrackQueue.next();

if (
prev.status === AudioPlayerStatus.Playing &&
player.state.status === AudioPlayerStatus.Idle
) {
if (nextTrack) {
TrackQueue.dequeue();
currTrack = TrackQueue.peek();
if (currTrack) {
player.play(currTrack.res);

await interaction.followUp({
ephemeral: false,
content: `Now playing... [**${currTrack.title}**](<${currTrack.path}>).`,
});
}
} else if (!nextTrack && TrackQueue.length() === 1) {
TrackQueue.dequeue();
}
}

if (
player.state.status === AudioPlayerStatus.Idle &&
TrackQueue.isEmpty()
) {
return;
}
});
} catch (err) {
console.log(err);

return await interaction.followUp({
ephemeral: true,
content:
"Something went wrong playing and maybe I crashed, but I'll be back... I hope so :anguished:",
});
}
// HACK: Proper refactor, this ugly af, `stateChange` already gives me access
// to previous states, so its redundant, but if not, loops until queue underflow
try {
const addedTrack = await Track.create(path);
TrackQueue.enqueue(addedTrack);
let currTrack = TrackQueue.peek();

if (player.state.status === AudioPlayerStatus.Idle) {
if (currTrack) {
player.play(currTrack.res);

await interaction.followUp({
ephemeral: false,
content: `Now playing... [**${currTrack.title}**](<${currTrack.path}>).`,
});
}
} else {
await interaction.followUp({
ephemeral: true,
content: `Added to queue... [**${addedTrack.title}**](<${addedTrack.path}>).`,
});
}

player.on("stateChange", async (prev) => {
const nextTrack = TrackQueue.next();

if (
prev.status === AudioPlayerStatus.Playing &&
player.state.status === AudioPlayerStatus.Idle
) {
if (nextTrack) {
TrackQueue.dequeue();
currTrack = TrackQueue.peek();
if (currTrack) {
player.play(currTrack.res);

await interaction.followUp({
ephemeral: false,
content: `Now playing... [**${currTrack.title}**](<${currTrack.path}>).`,
});
}
} else if (!nextTrack && TrackQueue.length() === 1) {
TrackQueue.dequeue();
}
}

if (
player.state.status === AudioPlayerStatus.Idle &&
TrackQueue.isEmpty()
) {
return;
}
});
} catch (err) {
console.log(err);

return await interaction.followUp({
ephemeral: true,
content:
"Something went wrong playing and maybe I crashed, but I'll be back... I hope so :anguished:",
});
}
i just wanted to bother with a last question, is there any way to clean up or refactor the code so its more concise and DRY?
Want results from more Discord servers?
Add your server