I am using the most recent version of the Discord.js API which requires the use of Discord.js/voice to play audio in a voice chat. I am trying to create my own music bot. I am, however, having trouble with actually playing the audio.
I think the issue is with how I create the AudioResource object although I have attempted to follow the examples on the discord guide.
Here are the relevant parts of the code:
const discord = require("discord.js")
const ytdl = require("ytdl-core")
const MUSIC_PATH = "./music/song.webm"
const {
createWriteStream,
createReadStream,
} = require("fs")
const {
joinVoiceChannel,
createAudioPlayer,
createAudioResource,
StreamType,
AudioPlayerStatus,
} = require("@discordjs/voice")
const {
prefix,
token
} = require("./config.json")
const client = new discord.Client({ intents: ["GUILDS", "GUILD_MESSAGES"] }) //Intention to interact with messages
const audioPlayer = {
musicStream: createAudioPlayer(),
connection: null,
connectionId: null,
}
client.on('messageCreate', msg => {
if (msg.author.bot || !msg.content.startsWith(prefix)) return
let messageParts = msg.content.split(" ")
const voiceChannel = msg.member.voice.channel
switch (messageParts[0]) {
case "!play":
if (!canExecutePlayRequest(msg, voiceChannel)) return
createChannelConnection(msg, voiceChannel)
playMusic(messageParts[1])
break;
case "!skip":
msg.reply("!skip")
break;
case "!stop":
msg.reply("!stop")
break;
case "!disconnect":
destroyChannelConnection(msg, voiceChannel)
break;
default:
msg.reply("That's not a real command!")
}
/**
* Creates connection object for channel that user is currently in. Adds said connection to audioPlayer.
* @param {*} msg Command message
* @param {*} voiceChannel Current voice channel of user
*/
function createChannelConnection(msg, voiceChannel) {
//Check for existing connection
if (audioPlayer.connection != null) {
//If already connected to channel of user return
if (audioPlayer.connectionId == voiceChannel.id) return //FIXME: channel checking update when user changes
//If connected to different channel destroy that connection first
destroyChannelConnection(msg, voiceChannel)
}
//Create and save connection
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: voiceChannel.guild.id,
adapterCreator: voiceChannel.guild.voiceAdapterCreator,
})
connection.subscribe(audioPlayer.musicStream)
audioPlayer.connection = connection
audioPlayer.connectionId = voiceChannel.id
}
})
function playMusic(url){
ytdl(url, { filter: 'audioonly' }).pipe(createWriteStream(MUSIC_PATH)) //works
const resource = createAudioResource(createReadStream(MUSIC_PATH), {
inputType: StreamType.WebmOpus,
})
console.log(resource)
audioPlayer.musicStream.play(resource)
}
Some notes:
I use my MUSIC_PATH instead of join(__dirname, 'file.webm') as they do on the discord guide I linked. I have used both and gotten the same output. Neither throws an error.
The bot is able to join the voice chat no problem. Having used audio status updates I have also concluded that the audioPlayer.musicStream.play() indeed causes the audio player to go into play mode.
Before executing a !play command the bot checks if it has connect and speak permissions which both pass.
This is the output of the console.log(resource) when attempting to play Joyner Lucas' Will by url:
AudioResource {
playbackDuration: 0,
started: false,
silenceRemaining: -1,
edges: [
{
type: 'webm/opus demuxer',
to: [Node],
cost: 1,
transformer: [Function: transformer],
from: [Node]
}
],
playStream: WebmDemuxer {
_readableState: ReadableState {
objectMode: true,
highWaterMark: 16,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: false,
ended: false,
endEmitted: false,
reading: false,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: true,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
_events: [Object: null prototype] {
prefinish: [Function: prefinish],
close: [Array],
end: [Function: onend],
finish: [Array],
error: [Array],
unpipe: [Function: onunpipe],
readable: [Function]
},
_eventsCount: 7,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: true,
_remainder: null,
_length: 0,
_count: 0,
_skipUntil: null,
_track: null,
_incompleteTrack: {},
_ebmlFound: false,
[Symbol(kCapture)]: false,
[Symbol(kCallback)]: null
},
metadata: null,
silencePaddingFrames: 5
}
Needless to say no music is played in the voice chat. What am I doing wrong in creating this resource? Clearly it's not working very well. Is it something to do with discordjs/opus? I've seen mentions of that floating around, but don't know anything about it although the dependency is included in my project.
Thank you in advance for the help.