Streaming ffmpeg output directly to dispatcher - javascript

I'm trying to play music with my discord bot and I want to use ffmpeg to specify the start of the music, which works perfectly fine, but I can only download the music with ffmpeg and then play it. I want ffmpeg to process it and then also stream it to play the music.
Here is the code I use to download and then play the music:
message.member.voiceChannel.join().then((con, err) => {
ytPlay.search_video(op, (id) => {
let stream = ytdl("https://www.youtube.com/watch?v=" + id, {
filter: "audioonly"
});
let audio = fs.createWriteStream('opStream.divx');
proc = new ffmpeg({
source: stream
})
proc.withAudioCodec('libmp3lame')
.toFormat('mp3')
.seekInput(35)
.output(audio)
.run();
proc.on('end', function() {
let input = fs.createReadStream('opStream.divx');
console.log('finished');
guild.queue.push(id);
guild.isPlaying = true;
guild.dispatcher = con.playStream(input);
});
});
})
Is it possible to do what I want and if yes how?

Instead of using ffmpeg to specify your starting point of the music you could use the seek StreamOptions of discord.js like:
const dispatcher = connection.play(ytdl(YOUR_URL, { filter: 'audioonly' }) , {seek:35})
This worked pretty fine for me

Yes is is possible, i made it in my bot.
First of all you need to install ytdl-core
Then create a play.js file where the stream function will be in.
This code will: take the youtube url and stream it without downloading the song, add the song to a queue, make the bot leave when the song is finished
Edit the code for what you need.
exports.run = async (client, message, args, ops) => {
if (!message.member.voiceChannel) return message.channel.send('You are not connected to a voice channel!');
if (!args[0]) return message.channel.send('Insert a URL!');
let validate = await ytdl.validateURL(args[0]);
let info = await ytdl.getInfo(args[0]);
let data = ops.active.get(message.guild.id) || {};
if (!data.connection) data.connection = await message.member.voiceChannel.join();
if(!data.queue) data.queue = [];
data.guildID = message.guild.id;
data.queue.push({
songTitle: info.title,
requester: message.author.tag,
url: args[0],
announceChannel: message.channel.id
});
if (!data.dispatcher) play(client, ops, data);
else {
message.channel.send(`Added to queue: ${info.title} | requested by: ${message.author.tag}`)
}
ops.active.set(message.guild.id, data);
}
async function play(client, ops, data) {
client.channels.get(data.queue[0].announceChannel).send(`Now Playing: ${data.queue[0].songTitle} | Requested by: ${data.queue[0].requester}`);
client.user.setActivity(`${data.queue[0].songTitle}`, {type: "LISTENING"});
data.dispatcher = await data.connection.playStream(ytdl(data.queue[0].url, {filter: 'audioonly'}));
data.dispatcher.guildID = data.guildID;
data.dispatcher.once('end', function() {
end(client, ops, this);
});
}
function end(client, ops, dispatcher){
let fetched = ops.active.get(dispatcher.guildID);
fetched.queue.shift();
if (fetched.queue.length > 0) {
ops.active.set(dispatcher.guildID, fetched);
play(client, ops, fetched);
} else {
ops.active.delete(dispatcher.guildID);
let vc = client.guilds.get(dispatcher.guildID).me.voiceChannel;
if (vc) vc.leave();
}
}
module.exports.help = {
name:"play"
}```

Related

How can I convert opus packets to mp3/wav

I created a discord bot with discord.js v13, I get trouble with converting the opus packet to other file types, even the discord.js official examples haven't updated for discord.js v13, I got no idea to deal with it, here is part of my code
async function record(interaction, opts = {}) {
//get voice connection, if there isn't one, create one
let connection = getVoiceConnection(interaction.guildId);
if (!connection) {
if (!interaction.member.voice.channel) return false;
connection = joinVoice(interaction.member.voice.channel, interaction)
}
const memberId = interaction.member.id;
//create the stream
const stream = connection.receiver.subscribe(memberId, {
end: {
behavior: EndBehaviorType.Manual
}
});
//create the file stream
const writableStream = fs.createWriteStream(`${opts.filename || interaction.guild.name}.${opts.format || 'opus'}`);
console.log('Created the streams, started recording');
//todo: set the stream into client and stop it in another function
return setTimeout(() => {
console.log('Creating the decoder')
let decoder = new prism.opus.Decoder();
console.log('Created');
stream.destroy();
console.log('Stopped recording and saving the stream');
stream
.pipe(writableStream)
stream.on('close', () => {
console.log('Data Stream closed')
});
stream.on('error', (e) => {
console.error(e)
});
}, 5000);
}
Try setting frameSize, channels and rate for the Decoder:
const opusDecoder = new prism.opus.Decoder({
frameSize: 960,
channels: 2,
rate: 48000,
})
Also not sure if it is intended, but you seem to destroy the stream just before you pipe it into writable stream.
Here is my example that gives stereo 48kHz signed 16-bit PCM stream:
const writeStream = fs.createWriteStream('samples/output.pcm')
const listenStream = connection.receiver.subscribe(userId)
const opusDecoder = new prism.opus.Decoder({
frameSize: 960,
channels: 2,
rate: 48000,
})
listenStream.pipe(opusDecoder).pipe(writeStream)
You can then use Audacity to play the PCM file. Use File -> Import -> Raw Data...

nodeJS - Unhandled rejection RequestError: Error: socket hang up

I have this code that is working fine for the first part. I'm tryng to send a video to the user after it select what video want to see. The problem is that I'm unable to achive it because I get always this error Unhandled rejection RequestError: Error: socket hang up.
#!/usr/bin/env node
const TelegramBot = require('node-telegram-bot-api');
const TorrentSearchApi = require('torrent-search-api');
const TorrentStream = require('torrent-stream');
const bot = new TelegramBot('5xxxx', {polling: true});
const providers = TorrentSearchApi.getProviders();
//console.log(providers);
TorrentSearchApi.enablePublicProviders();
let magnetLinks = []
let response = 'Here are your search results: \n';
let engine;
let stream;
// Matches "/echo [whatever]"
bot.onText(/\/search (.+)/, async (msg, match) => {
// 'msg' is the received Message from Telegram
// 'match' is the result of executing the regexp above on the text content
// of the message
console.log(match[1]);
TorrentSearchApi.search(match[1], 'All', 10).then( (results) => {
results.forEach( (item, index) => {
if( item.seeds > 1 ){
response += `${index}) ${item.title}\n`;
magnetLinks.push(item.magnet);
}
});
bot.sendMessage(msg.chat.id, response).then( (message) => {
bot.sendMessage(msg.chat.id, 'Use the /stream command followed by the number of the torrent file you want to play');
});
});
});
bot.onText(/\/stream (.+)/, async (msg, match) => {
console.log(magnetLinks[match[1]]);
engine = TorrentStream(magnetLinks[match[1]]);
engine.on('ready', () => {
engine.files.forEach( (file) => {
console.log('filename:', file.name);
stream = file.createReadStream();
console.log(stream);
// stream is readable stream to containing the file content
bot.sendVideo(msg.chat.id, stream);
});
});
});
How I can solve the problem and let the telegram bot send the video file to the user?Is possible to stream a video file with a telegram bot?

Discord.js record MP3 of voice channel?

I'm trying to make a record command in my discord.js bot. My code so far is:
const channel = message.member.voice.channel;
if(!channel) return message.channel.send('Join a VC first!');
const connection = await channel.join();
const receiver = connection.receiver.createStream(message.member, {
mode: "pcm",
end: "silence"
});
const writer = receiver.pipe(fs.createWriteStream('./recording.pcm'));
writer.on('finish', () => {
channel.leave();
message.channel.send('It went quiet, so I left...');
});
That saves recording.pcm to my PC. If I try to open the file in windows media player or anything, it doesn't recognise the file type. I used Audacity import raw audio function, and I could hear my recording, so I know it works. However, giving a user that type of file is very inconvenient. How can I turn this .pcm file into a .wav or .mp3 in node.js? Thanks!
You could use ffmpeg - npm i ffmpeg
const ffmpeg = require('ffmpeg');
try {
var process = new ffmpeg('path/to/pcm/file');
process.then(function (audio) {
audio.fnExtractSoundToMP3('path/to/new/file.mp3', function (error, file) {
if (!error) console.log('Audio File: ' + file);
});
}, function (err) {
console.log('Error: ' + err);
});
} catch (e) {
console.log(e);
}
This should save the new mp3 file to the specified location.

How do I playback .m4s files. And turn them into a binary stream for a Discord Bot

I want to download the BBC radio 2 and stream it to a discord server. Each request is https://as-dash-uk-live.akamaized.net/pool_904/live/uk/bbc_radio_two/bbc_radio_two.isml/dash/bbc_radio_two-audio=320000-250636302.m4s However the Time stamp will change and they regularly delete content. I found a .dash file as well https://as-dash-uk-live.akamaized.net/pool_904/live/uk/bbc_radio_two/bbc_radio_two.isml/dash/bbc_radio_two-audio=320000.dash which is seems to request. However I can't find how to send it through a discord bot to a discord server. My code is
const axios = require('axios')
module.exports = {
play: async function (guild,message) {
const serverQueue = {};
const voiceChannel = message.member.voice.channel
serverQueue.connection = await voiceChannel.join();
plays(guild)
async function plays(guild) {
let data1 = new Date(1353439725);
let data2 = new Date(Date.now()/1000);
console.log(`https://as-dash-uk-live.akamaized.net/pool_904/live/uk/bbc_radio_two/bbc_radio_two.isml/dash/bbc_radio_two-audio=320000-${Math.floor((data2 - data1))-4000}.m4s`)
const dispatcher = serverQueue.connection.play((await axios.get(`https://as-dash-uk-live.akamaized.net/pool_904/live/uk/bbc_radio_two/bbc_radio_two.isml/dash/bbc_radio_two-audio=320000-${Math.floor((data2 - data1))-4000}.m4s`)).data)
.on('finish', () => {
console.log('Music ended!');
plays(guild);
return
})
.on('error', error => {
console.error(error);
plays(guild);
return
});
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
}
}
}
However no audio plays and the audio end event is always being called. Am I being really stupid or is there a way to do this.

I'm using ytdl-core for a music bot, and it keeps outputting that play is not defined

I am using ytdl-core and node-opus to add music functionality to my bot. I am also following a tutorial. Up until I started to add queuing functionality, the bot worked fine. As I integrated the queuing, the bot could still join and leave voice channels, but can't play music. It outputs (node:22116) UnhandledPromiseRejectionWarning: ReferenceError: play is not defined
As per comments on the video, I have tried switching play to playstream. This worked originally, but doesn't help, only outputting that it is not defined.
Here is the command:
if (!message.member.voiceChannel) return message.channel.send("You must be connected to a voice channel.");
if (!args[0]) return message.channel.send("You must supply a __valid__ URL.");
let validate = await ytdl.validateURL(args[0]);
if (!validate) return message.channel.send("You must supply a __valid__ URL.");
let info = await ytdl.getInfo(args[0]);
let data = active.get(message.guild.id) || {};
if (!data.connection) data.connection = await message.member.voiceChannel.join();
if (!data.queue) data.queue = [];
data.guildID = message.guild.id;
data.queue.push ({
songTitle: info.title,
requester: message.author.tag,
url: args[0],
announceChannel: message.channel.id
});
if (!data.dispatcher) play();
else {
message.channel.send(`Added song to queue: ${info.title} || Requested by: ${message.author.id}`);
active.set(message.guild.id, data);
I'd like to be able to still follow the tutorial to fully integrate the queuing.
You didn't define the play() function in previous code, so you also can't use it.
Here is an example of how your play() function could look:
const queue = msg.client.queue;
const ytdl = require('ytdl-core');
async function play(guild, song) {
const serverQueue = await queue.get(guild.id);
if (!song) {
await serverQueue.voiceChannel.leave();
await queue.delete(guild.id);
return;
}
const stream = await ytdl(song.url, {
filter: 'audioonly'
});
const dispatcher = await serverQueue.connection.playStream(stream)
.on('end', async reason => {
if (reason === 'Stream is not generating quickly enough.');
serverQueue.songs.shift('Stream is not generating quickly enough');
await play(guild, serverQueue.songs[0]);
})
.on('error', error => console.error(error));
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
}
My queue is constructed as following:
const queueConstruct = {
textChannel: msg.channel,
voiceChannel: voiceChannel,
connection: null,
songs: [],
volume: 2,
playing: true
};
It could be that you have to change some lines of code, so that it works for your bot!

Categories