Oliver
Oliver

Reputation: 530

Playing an audio file using discord.js and ytdl-core

I'm trying to download and play an audio file fetched from youtube using ytdl and discord.js:

        ytdl(url)
            .pipe(fs.createWriteStream('./music/downloads/music.mp3'));

        var voiceChannel = message.member.voiceChannel;
        voiceChannel.join().then(connection => {
            console.log("joined channel");
            const dispatcher = connection.playFile('./music/downloads/music.mp3');
            dispatcher.on("end", end => {
                console.log("left channel");
                voiceChannel.leave();
            });
        }).catch(err => console.log(err));
        isReady = true

I successfully manage to play the mp3 file in ./music/downloads/ without the ytdl part (ytdl(url).pipe(fs.createWriteStream('./music/downloads/music.mp3'));). But when that part is in the code, the bot just joins and leaves.

Here is the output with the ytdl part:

Bot has started, with 107 users, in 43 channels of 3 guilds.
joined channel
left channel

And here is the output without the ytdl part:

Bot has started, with 107 users, in 43 channels of 3 guilds.
joined channel
[plays mp3 file]
left channel

Why is that and how can i solve it?

Upvotes: 2

Views: 27172

Answers (2)

user9258013
user9258013

Reputation:

You're doing it in an inefficient way. There's no synchronization between reading and writing.
Wait for the file to be written to the filesystem, then read it!

Directly stream it

Redirect YTDL's video output to dispatcher, which would be converted to opus audio data packets first, then streamed from your computer to Discord.

message.member.voiceChannel.join()
.then(connection => {
    console.log('joined channel');

    connection.playStream(ytdl(url))
    // When no packets left to send, leave the channel.
    .on('end', () => {
        console.log('left channel');
        connection.channel.leave();
    })
    // Handle error without crashing the app.
    .catch(console.error);
})
.catch(console.error);

FWTR (First write, then read)

The approach you used wass pretty close to success, but the failure is when you don't synchronize read/write.

var stream = ytdl(url);

// Wait until writing is finished
stream.pipe(fs.createWriteStream('tmp_buf_audio.mp3'))
.on('end', () => {
    message.member.voiceChannel.join()
    .then(connection => {
        console.log('joined channel');

        connection.playStream(fs.createReadStream('tmp_buf_audio.mp3'))
        // When no packets left to send, leave the channel.
        .on('end', () => {
            console.log('left channel');
            connection.channel.leave();
        })
        // Handle error without crashing the app.
        .catch(console.error);
    })
    .catch(console.error);
});

Upvotes: 1

ufxmeng
ufxmeng

Reputation: 2600

Use playStream instead of playFile when you need to play a audio stream.

const streamOptions = { seek: 0, volume: 1 };
var voiceChannel = message.member.voiceChannel;
        voiceChannel.join().then(connection => {
            console.log("joined channel");
            const stream = ytdl('https://www.youtube.com/watch?v=gOMhN-hfMtY', { filter : 'audioonly' });
            const dispatcher = connection.playStream(stream, streamOptions);
            dispatcher.on("end", end => {
                console.log("left channel");
                voiceChannel.leave();
            });
        }).catch(err => console.log(err));

Upvotes: 6

Related Questions