Merge pull request #5 from sravanth-chebrolu/fix-multiple-user-recording
Fixed multi user issue. merge.js now merges all chunks.
This commit is contained in:
commit
d3c8e69ec3
55
README.md
55
README.md
|
@ -1,42 +1,44 @@
|
|||
# Discord Voice Recorder
|
||||
|
||||
A tiny [Discord.js](https://discord.js.org/#/) script that can record your voice calls. Jump to [Installation & Usage](https://github.com/sravanth-chebrolu/discord-voice-recorder#installation-and-usage) to learn how to run the script and save the audio to your local device.
|
||||
A tiny [Discord.js](https://discord.js.org/#/) script that can record discord voice calls. Just summon the bot and to the voice channel and record a multi-user call! Jump to [Installation & Usage](https://github.com/sravanth-chebrolu/discord-voice-recorder#installation-and-usage) to learn how to run the script and save the audio to your local device.
|
||||
|
||||
<img src="https://i.imgur.com/y6JCNNA.png" width="400" align="center">
|
||||
|
||||
### Content
|
||||
|
||||
- [Installation and Usage](#installation-and-usage)
|
||||
- [Installing](#installing)
|
||||
- [Download the Source and Install FFmpeg](#download-the-source-and-install-ffmpeg)
|
||||
- [Setting Up the Local Environment](#setting-up-the-local-environment)
|
||||
- [Running the Script](#running-the-script)
|
||||
- [Start Recording](#start-recording)
|
||||
- [Stop Recording](#stop-recording)
|
||||
- [Managing the Output](#managing-the-output)
|
||||
- [Merge Recording](#merge-recording)
|
||||
- [Convert the Merged File to MP3](#convert-the-merged-file-to-mp3)
|
||||
- [Thanks](#thanks)
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
### Installing the Source Code
|
||||
|
||||
Download the ZIP file and extract the contents to a folder or just clone the repository to your local storage using this command:
|
||||
### Download the Source and Install FFmpeg
|
||||
|
||||
Clone the repository :
|
||||
```
|
||||
git clone https://github.com/sravanth-chebrolu/discord-voice-recorder/
|
||||
```
|
||||
|
||||
After extracting/cloning, run `npm i` to install the dependencies. **Ensure that you create a folder by the name** `recordings` **at the root directory.**
|
||||
Run `npm i` to install the dependent `node_modules`.
|
||||
|
||||
Next head over to the [FFmpeg.org](https://ffmpeg.org/download.html), and download the executables depending on your OS; If you're on Windows ensure sure that you have included the path to the bin folder to your system enviroment PATH variables. Ex: `C:\Programs\ffmpeg\ffmpeg-20200831-4a11a6f-win64-static\bin`.
|
||||
Next head over to the [FFmpeg.org](https://ffmpeg.org/download.html), and download the executables for your OS; If you're on Windows, double check if ffmpeg bin is on your path.
|
||||
|
||||
### Setting Up the Local Environment
|
||||
|
||||
To run this script locally you will need to [create a discord bot](https://discordpy.readthedocs.io/en/latest/discord.html) first. After creating it invite the bot to your server(to avoid potential problems, it is recommended that the bot be given adminstrator privilege when creating the initve link). Follow the next steps to finish the setup:
|
||||
To run this script locally you will need to [create a discord bot](https://discordpy.readthedocs.io/en/latest/discord.html) first. Invite your bot to your server(to avoid potential problems, it is recommended that the bot be given adminstrator privileges when creating the initve link). Follow the next steps to finish the setup:
|
||||
|
||||
1. Create a `config.json` file at the root directory.
|
||||
1. Create a `config.json` file and a `recordings` folder at the root directory.
|
||||
2. Copy the the bot token from the [developer window](https://discord.com/developers/applications).
|
||||
3. Choose a prefix you want the bot to answer to.
|
||||
|
||||
Paste the bot token and the prefix value so your config.json will look like this:
|
||||
Paste the bot token and the prefix value to your config.json, like this:
|
||||
|
||||
```
|
||||
{
|
||||
|
@ -45,7 +47,7 @@ Paste the bot token and the prefix value so your config.json will look like this
|
|||
}
|
||||
```
|
||||
|
||||
And you're all set. Note that all your recordings will be saved to `~/recordings/` if `~` is the root directory.
|
||||
And you're all set. Note that all your recordings will be saved to the `recordings` directory.
|
||||
|
||||
### Running the Script
|
||||
|
||||
|
@ -60,12 +62,10 @@ The bot should be online and you can run the bot commands in your discord server
|
|||
#### Start Recording
|
||||
|
||||
```
|
||||
<PREFIX>enter <VOICE_CHANNEL_NAME> <AUDIO_FILE_NAME>
|
||||
<PREFIX>enter <VOICE_CHANNEL_NAME>
|
||||
```
|
||||
|
||||
This will summon the bot into the voice channel mentioned in the arguments and start recording the audio. You will hear a 'ding' when you run the command indicating that the connection has been established. If you don't hear the 'ding', there's a problem with the ffmpeg installation. Keep in mind that the audio will be recorded in the [PCM format](https://en.wikipedia.org/wiki/Pulse-code_modulation) and will be saved to `~/recordings/AUDIO_FILE_NAME.pcm`.
|
||||
|
||||
> To work with PCM audio, you could use software such as [Audacity](https://www.audacityteam.org/). To import the audio into Audacity, open File > Import > Raw Data... and then select your audio file. You should select Signed 16-bit PCM as the encoding, a Little-endian byte order, 2 Channels (Stereo) and a sample rate of 48000Hz.
|
||||
This will summon the bot into the voice channel mentioned in the arguments and start recording the audio. You should hear a 'drop' sound when you run the command indicating that the connection has been established. If you don't hear the sound, there's a problem with the ffmpeg installation.
|
||||
|
||||
#### Stop Recording
|
||||
|
||||
|
@ -73,7 +73,30 @@ This will summon the bot into the voice channel mentioned in the arguments and s
|
|||
<PREFIX>exit <VOICE_CHANNEL_NAME>
|
||||
```
|
||||
|
||||
This will remove the bot from the mentioned voice channel and the recording will stop. Note that if you re-summon the bot with the same file name, the new audio will be appended to the end of the provious file.
|
||||
This will remove the bot from the mentioned voice channel and the recording will stop.
|
||||
|
||||
### Managing the Output
|
||||
|
||||
Keep in mind that the audio will be recorded in the [PCM format](https://en.wikipedia.org/wiki/Pulse-code_modulation).
|
||||
|
||||
> To work with PCM audio, you could use software such as [Audacity](https://www.audacityteam.org/). To import the audio into Audacity, open File > Import > Raw Data... and then select your audio file. You should select Signed 16-bit PCM as the encoding, a Little-endian byte order, 2 Channels (Stereo) and a sample rate of 48000Hz.
|
||||
|
||||
#### Merge Recording
|
||||
|
||||
```
|
||||
node ./bin/merge.js
|
||||
```
|
||||
|
||||
This will generate a merged output of all the individual PCM output files to a `merge.pcm` file in the `recordings` directory.
|
||||
|
||||
#### Convert the Merged File to MP3
|
||||
|
||||
Since FFmpeg is a dependency, we can use it to convert the raw `.pcm` file to a more user friendly `.mp3` format, mentioned in #3 this can be done using:
|
||||
|
||||
```
|
||||
ffmpeg -f s16le -ar 44.1k -ac 2 -i merge.pcm output.mp3
|
||||
```
|
||||
|
||||
|
||||
# Thanks
|
||||
|
||||
|
|
49
bin/commands.js
Normal file
49
bin/commands.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
const fs = require('fs');
|
||||
|
||||
const createNewChunk = () => {
|
||||
const pathToFile = __dirname + `/../recordings/${Date.now()}.pcm`;
|
||||
return fs.createWriteStream(pathToFile);
|
||||
};
|
||||
|
||||
exports.enter = function(msg, channelName) {
|
||||
const voiceChannel = msg.guild.channels.cache.find(channel => channel.name === channelName);
|
||||
|
||||
if (!voiceChannel || voiceChannel.type !== 'voice')
|
||||
return msg.reply(`The channel #${channelName} doesn't exist or isn't a voice channel.`);
|
||||
|
||||
console.log(`Sliding into ${voiceChannel.name} ...`);
|
||||
voiceChannel.join()
|
||||
.then(conn => {
|
||||
|
||||
const dispatcher = conn.play('../sounds/drop.mp3');
|
||||
dispatcher.on('finish', () => { console.log(`Joined ${voiceChannel.name}!\n\nREADY TO RECORD\n`); });
|
||||
|
||||
const receiver = conn.receiver;
|
||||
conn.on('speaking', (user, speaking) => {
|
||||
if (speaking) {
|
||||
console.log(`${user.username} started speaking`);
|
||||
const audioStream = receiver.createStream(user, { mode: 'pcm' });
|
||||
audioStream.pipe(createNewChunk());
|
||||
audioStream.on('end', () => { console.log(`${user.username} stopped speaking`); });
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => { throw err; });
|
||||
}
|
||||
|
||||
exports.exit = function(msg, channelName) {
|
||||
const voiceChannel = msg.guild.channels.cache.find(channel => channel.name === channelName);
|
||||
|
||||
if (!voiceChannel || voiceChannel.type !== 'voice')
|
||||
return msg.reply(`The channel #${channelName} doesn't exist or isn't a voice channel.`);
|
||||
|
||||
voiceChannel.join()
|
||||
.then(conn => {
|
||||
const dispatcher = conn.play('../sounds/badumtss.mp3', { volume: 0.45 });
|
||||
dispatcher.on('finish', () => {
|
||||
voiceChannel.leave();
|
||||
console.log(`\nSTOPPED RECORDING\n`);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
26
bin/merge.js
Normal file
26
bin/merge.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
var fs = require('fs'),
|
||||
chunks = fs.readdirSync('../recordings'),
|
||||
inputStream,
|
||||
currentfile,
|
||||
outputStream = fs.createWriteStream('../recordings/merge.pcm');
|
||||
|
||||
chunks.sort((a, b) => { return a - b; });
|
||||
|
||||
function appendFiles() {
|
||||
if (!chunks.length) {
|
||||
outputStream.end(() => console.log('Finished.'));
|
||||
return;
|
||||
}
|
||||
|
||||
currentfile = '../recordings/' + chunks.shift();
|
||||
inputStream = fs.createReadStream(currentfile);
|
||||
|
||||
inputStream.pipe(outputStream, { end: false });
|
||||
|
||||
inputStream.on('end', function() {
|
||||
console.log(currentfile + ' appended');
|
||||
appendFiles();
|
||||
});
|
||||
}
|
||||
|
||||
appendFiles();
|
44
index.js
44
index.js
|
@ -1,52 +1,16 @@
|
|||
const Discord = require("discord.js");
|
||||
const Discord = require('discord.js');
|
||||
const client = new Discord.Client();
|
||||
|
||||
const fs = require('fs');
|
||||
const config = require('./config.json');
|
||||
|
||||
const appendChunkToFile = (fileName) => {
|
||||
const pathToFile = __dirname + `/recordings/${fileName}.pcm`;
|
||||
return fs.createWriteStream(pathToFile, { flags: 'a' });
|
||||
};
|
||||
const commands = require(`./bin/commands`);
|
||||
|
||||
client.on('message', msg => {
|
||||
if (msg.content.startsWith(config.PREFIX)) {
|
||||
const commandBody = msg.content.substring(config.PREFIX.length).split(' ');
|
||||
const channelName = commandBody[1];
|
||||
|
||||
if (commandBody[0] === ('enter') && commandBody[1] && commandBody[2]) {
|
||||
const voiceChannel = msg.guild.channels.cache.find(channel => channel.name === channelName);
|
||||
|
||||
if (!voiceChannel || voiceChannel.type !== 'voice')
|
||||
return msg.reply(`The channel #${channelName} doesn't exist or isn't a voice channel.`);
|
||||
|
||||
console.log(`Sliding into ${voiceChannel.name}...`);
|
||||
voiceChannel.join()
|
||||
.then(conn => {
|
||||
|
||||
const dispatcher = conn.play('./sounds/ding.mp3');
|
||||
dispatcher.on('start', () => { console.log('ding.mp3 is playing..'); });
|
||||
dispatcher.on('finish', () => { console.log('ding.mp3 has finished playing..'); });
|
||||
console.log(`Joined ${voiceChannel.name}!\n\nREADY TO RECORD\n`);
|
||||
|
||||
const receiver = conn.receiver;
|
||||
conn.on('speaking', (user, speaking) => {
|
||||
if (speaking) {
|
||||
console.log(`${user.username} started speaking`);
|
||||
const audioStream = receiver.createStream(user, { mode: 'pcm' });
|
||||
audioStream.pipe(appendChunkToFile(commandBody[2]));
|
||||
audioStream.on('end', () => { console.log(`${user.username} stopped speaking`); });
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => { throw err; });
|
||||
}
|
||||
if (commandBody[0] === ('exit') && commandBody[1]) {
|
||||
const voiceChannel = msg.guild.channels.cache.find(channel => channel.name === channelName);
|
||||
console.log(`Slipping out of ${voiceChannel.name}...`);
|
||||
voiceChannel.leave();
|
||||
console.log(`\nSTOPPED RECORDING\n`);
|
||||
}
|
||||
if (commandBody[0] === ('enter') && commandBody[1]) commands.enter(msg, channelName);
|
||||
if (commandBody[0] === ('exit') && commandBody[1]) commands.exit(msg, channelName);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "discord-bot",
|
||||
"name": "discord-voice-recorder",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
|
@ -343,9 +343,9 @@
|
|||
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.3",
|
||||
|
|
15
sounds/LICENSE
Normal file
15
sounds/LICENSE
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
Hello from Orange Free Sounds,
|
||||
|
||||
|
||||
Stock audio - Free sound effects, loops and music.
|
||||
|
||||
|
||||
There are no hidden costs or need to sign-up.
|
||||
|
||||
|
||||
|
||||
License: The sound effect is permitted for non-commercial use under license “Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)
|
||||
|
||||
|
||||
http://www.orangefreesounds.com/
|
BIN
sounds/badumtss.mp3
Normal file
BIN
sounds/badumtss.mp3
Normal file
Binary file not shown.
BIN
sounds/ding.mp3
BIN
sounds/ding.mp3
Binary file not shown.
BIN
sounds/drop.mp3
Normal file
BIN
sounds/drop.mp3
Normal file
Binary file not shown.
Loading…
Reference in a new issue