🔀 Merge branch develop into master
This commit is contained in:
commit
97bfd52218
10 changed files with 1054 additions and 907 deletions
22
.eslintrc.js
Normal file
22
.eslintrc.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
commonjs: true,
|
||||||
|
es6: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'standard'
|
||||||
|
],
|
||||||
|
globals: {
|
||||||
|
Atomics: 'readonly',
|
||||||
|
SharedArrayBuffer: 'readonly'
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 11
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 4],
|
||||||
|
'no-async-promise-executor': 'off',
|
||||||
|
'no-unused-vars': 'off'
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,14 +15,14 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: andstor/jsdoc-action@v1
|
uses: andstor/jsdoc-action@v1
|
||||||
with:
|
with:
|
||||||
source_dir: ./src
|
|
||||||
output_dir: ./docs
|
output_dir: ./docs
|
||||||
config_file: .jsdoc.json
|
config_file: .jsdoc.json
|
||||||
template_name: Androz2091/minami
|
template: Androz2091/minami
|
||||||
front_page: README.md
|
front_page: README.md
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
personal_token: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
personal_token: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||||
publish_dir: ./docs
|
publish_dir: ./docs
|
||||||
|
cname: discord-player.js.org
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,3 +2,5 @@ node_modules
|
||||||
poc.js
|
poc.js
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
docs
|
||||||
|
.vscode
|
||||||
|
|
524
README.md
524
README.md
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
**Note**: this module uses recent discordjs features and requires discord.js version 12.
|
**Note**: this module uses recent discordjs features and requires discord.js version 12.
|
||||||
|
|
||||||
Discord Player is a powerful [Node.js](https://nodejs.org) module that allows you to easily implement music commands. **Everything** is customizable, and everything is done to simplify your work **without limiting you**!
|
Discord Player is a powerful [Node.js](https://nodejs.org) module that allows you to easily implement music commands. **Everything** is customizable, and everything is done to simplify your work **without limiting you**! It doesn't require any api key, as it uses **scraping**.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -14,15 +14,24 @@ Discord Player is a powerful [Node.js](https://nodejs.org) module that allows yo
|
||||||
npm install --save discord-player
|
npm install --save discord-player
|
||||||
```
|
```
|
||||||
|
|
||||||
Install **opusscript** or **@discordjs/opus**:
|
Install **@discordjs/opus**:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install --save opusscript
|
npm install --save @discordjs/opus
|
||||||
```
|
```
|
||||||
|
|
||||||
Install [FFMPEG](https://www.ffmpeg.org/download.html) and you're done!
|
Install [FFMPEG](https://www.ffmpeg.org/download.html) and you're done!
|
||||||
|
|
||||||
## Player
|
## Features
|
||||||
|
|
||||||
|
🤘 Easy to use!
|
||||||
|
🎸 You can apply some cool filters (bassboost, reverse, 8D, etc...)
|
||||||
|
🎼 Manage your server queues with simple functions (add songs, skip the current song, pause the music, resume it, etc...)!
|
||||||
|
🌐 Multi-servers support
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Here is the code you will need to get started with discord-player. Then, you will be able to use `client.player` everywhere in your code!
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const Discord = require("discord.js"),
|
const Discord = require("discord.js"),
|
||||||
|
@ -33,8 +42,8 @@ settings = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const { Player } = require("discord-player");
|
const { Player } = require("discord-player");
|
||||||
// Create a new Player (Youtube API key is your Youtube Data v3 key)
|
// Create a new Player (you don't need any API Key)
|
||||||
const player = new Player(client, "YOUTUBE API KEY");
|
const player = new Player(client);
|
||||||
// To easily access the player
|
// To easily access the player
|
||||||
client.player = player;
|
client.player = player;
|
||||||
|
|
||||||
|
@ -42,84 +51,7 @@ client.on("ready", () => {
|
||||||
console.log("I'm ready !");
|
console.log("I'm ready !");
|
||||||
});
|
});
|
||||||
|
|
||||||
client.login(settings.token);
|
client.on("message", async (message) => {
|
||||||
```
|
|
||||||
|
|
||||||
You can pass a third parameter when instantiating the class Player: the **options** object:
|
|
||||||
**options.leaveOnEnd**: whether the bot should leave the voice channel when there is no more song in the queue.
|
|
||||||
**options.leaveOnStop**: whether the bot should leave the voice channel when the `stop()` function is used.
|
|
||||||
**options.leaveOnEmpty**: whether the bot should leave the voice channel if there is no more member in it.
|
|
||||||
|
|
||||||
### Features Overview
|
|
||||||
|
|
||||||
You need to **init the guild queue using the play() function**, then you are able to manage the queue using the following functions:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// Play a song in the voice channel and init the guild queue
|
|
||||||
client.player.play(voiceChannel, songName);
|
|
||||||
|
|
||||||
// Add a song to the queue
|
|
||||||
client.player.addToQueue(guildID, songName);
|
|
||||||
// Clear the queue
|
|
||||||
client.player.clearQueue(guildID);
|
|
||||||
// Get the queue
|
|
||||||
client.player.getQueue(guildID);
|
|
||||||
// Skip the current song
|
|
||||||
client.player.skip(guildID);
|
|
||||||
// Remove a song from the queue using the index number
|
|
||||||
client.player.remove(guildID, song);
|
|
||||||
|
|
||||||
|
|
||||||
// Pause
|
|
||||||
client.player.pause(guildID);
|
|
||||||
// Resume
|
|
||||||
client.player.resume(guildID);
|
|
||||||
// Stop
|
|
||||||
client.player.stop(guildID);
|
|
||||||
|
|
||||||
// Check if music is playing in a guild
|
|
||||||
client.player.isPlaying(guildID);
|
|
||||||
// Get the currently playing song
|
|
||||||
client.player.nowPlaying(guildID);
|
|
||||||
|
|
||||||
|
|
||||||
// Current song will be repeated indefinitely
|
|
||||||
client.player.setRepeatMode(guildID, true);
|
|
||||||
// Current song will no longer be repeated indefinitely
|
|
||||||
client.player.setRepeatMode(guildID, false);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Event messages
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.getQueue(guildID)
|
|
||||||
.on('end', () => {
|
|
||||||
message.channel.send('There is no more music in the queue!');
|
|
||||||
})
|
|
||||||
.on('songChanged', (oldSong, newSong) => {
|
|
||||||
message.channel.send(`Now playing ${newSong.name}...`);
|
|
||||||
})
|
|
||||||
.on('channelEmpty', () => {
|
|
||||||
message.channel.send('Stop playing, there is no more member in the voice channel...');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Play
|
|
||||||
|
|
||||||
To play a song, use the `client.player.play()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.play(voiceChannel, songName, requestedBy);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
||||||
const command = args.shift().toLowerCase();
|
const command = args.shift().toLowerCase();
|
||||||
|
@ -127,389 +59,65 @@ client.on('message', async (message) => {
|
||||||
// !play Despacito
|
// !play Despacito
|
||||||
// will play "Despacito" in the member voice channel
|
// will play "Despacito" in the member voice channel
|
||||||
|
|
||||||
if(command === 'play'){
|
if(command === "play"){
|
||||||
let song = await client.player.play(message.member.voice.channel, args[0], message.member.user.tag);
|
let track = await client.player.play(message.member.voice.channel, args[0], message.member.user.tag);
|
||||||
message.channel.send(`Currently playing ${song.name}! - Requested by ${song.requestedBy}`);
|
message.channel.send(`Currently playing ${track.name}! - Requested by ${track.requestedBy}`);
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pause
|
|
||||||
|
|
||||||
To pause the current song, use the `client.player.pause()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.pause(guildID);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'pause'){
|
|
||||||
let song = await client.player.pause(message.guild.id);
|
|
||||||
message.channel.send(`${song.name} paused!`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.login(settings.token);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Resume
|
## [Documentation](https://discord-player.js.org)
|
||||||
|
|
||||||
To resume the current song, use the `client.player.resume()` function.
|
You will find many examples in the documentation to understand how the package works!
|
||||||
|
|
||||||
**Usage:**
|
### Methods overview
|
||||||
|
|
||||||
|
You need to **init the guild queue using the play() function**, then you are able to manage the queue and the music using the following functions. Click on a function name to get an example code and explanations.
|
||||||
|
|
||||||
|
#### Queue initialization
|
||||||
|
|
||||||
|
* [play(voiceChannel, track, requestedBy)](https://discord-player.js.org/Player.html#play) - play a track in a server
|
||||||
|
|
||||||
|
#### Queue management
|
||||||
|
|
||||||
|
* [isPlaying(guildID)](https://discord-player.js.org/Player.html#isPlaying) - check if there is a queue for a specific server
|
||||||
|
|
||||||
|
#### Manage tracks in your queue
|
||||||
|
|
||||||
|
* [getQueue(guildID)](https://discord-player.js.org/Player.html#getQueue) - get the server queue
|
||||||
|
* [addToQueue(guildID, track, requestedBy)](https://discord-player.js.org/Player.html#addToQueue) - add a track to the server queue
|
||||||
|
* [clearQueue(guildID)](https://discord-player.js.org/Player.html#clearQueue) - clear the server queue
|
||||||
|
* [remove(guildID, track)](https://discord-player.js.org/Player.html#remove) - remove a track from the server queue
|
||||||
|
* [nowPlaying(guildID)](https://discord-player.js.org/Player.html#nowPlaying) - get the current track
|
||||||
|
|
||||||
|
#### Manage music stream
|
||||||
|
|
||||||
|
* [skip(guildID)](https://discord-player.js.org/Player.html#skip) - skip the current track
|
||||||
|
* [pause(guildID)](https://discord-player.js.org/Player.html#pause) - pause the current track
|
||||||
|
* [resume(guildID)](https://discord-player.js.org/Player.html#resume) - resume the current track
|
||||||
|
* [stop(guildID)](https://discord-player.js.org/Player.html#stop) - stop the current track
|
||||||
|
* [setFilters(data)](https://discord-player.js.org/Player.html#setFilters) - update filters (bassboost for example)
|
||||||
|
* [setRepeatMode(boolean)](https://discord-player.js.org/Player.html#setRepeatMode) - enable or disable repeat mode for the server
|
||||||
|
|
||||||
|
### Event messages
|
||||||
|
|
||||||
```js
|
```js
|
||||||
client.player.resume(guildID);
|
// Play the music
|
||||||
```
|
await client.player.play(message.member.voice.channel, "Despacito")
|
||||||
|
|
||||||
**Example**:
|
// Then add some messages that will be sent when the events will be triggered
|
||||||
|
client.player.getQueue(guildID)
|
||||||
```js
|
.on('end', () => {
|
||||||
client.on('message', async (message) => {
|
message.channel.send('There is no more music in the queue!');
|
||||||
|
})
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
.on('trackChanged', (oldTrack, newTrack) => {
|
||||||
const command = args.shift().toLowerCase();
|
message.channel.send(`Now playing ${newTrack.name}...`);
|
||||||
|
})
|
||||||
if(command === 'resume'){
|
.on('channelEmpty', () => {
|
||||||
let song = await client.player.resume(message.guild.id);
|
message.channel.send('Stop playing, there is no more member in the voice channel...');
|
||||||
message.channel.send(`${song.name} resumed!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stop
|
|
||||||
|
|
||||||
To stop the music, use the `client.player.stop()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.stop(guildID);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'stop'){
|
|
||||||
client.player.stop(message.guild.id);
|
|
||||||
message.channel.send('Music stopped!');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### SetVolume
|
|
||||||
|
|
||||||
To update the volume, use the `client.player.setVolume()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.setVolume(guildID, percent);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'setvolume'){
|
|
||||||
client.player.setVolume(message.guild.id, parseInt(args[0]));
|
|
||||||
message.channel.send(`Volume set to ${args[0]} !`);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### AddToQueue
|
|
||||||
|
|
||||||
To add a song to the queue, use the `client.player.addToQueue()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.addToQueue(guildID, songName);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
In this example, you will see how to add a song to the queue if one is already playing.
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'play'){
|
|
||||||
let aSongIsAlreadyPlaying = client.player.isPlaying(message.guild.id);
|
|
||||||
// If there's already a song playing
|
|
||||||
if(aSongIsAlreadyPlaying){
|
|
||||||
// Add the song to the queue
|
|
||||||
let song = await client.player.addToQueue(message.guild.id, args[0]);
|
|
||||||
message.channel.send(`${song.name} added to queue!`);
|
|
||||||
} else {
|
|
||||||
// Else, play the song
|
|
||||||
let song = await client.player.play(message.member.voice.channel, args[0]);
|
|
||||||
message.channel.send(`Currently playing ${song.name}!`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### ClearQueue
|
|
||||||
|
|
||||||
To clear the queue, use the `client.player.clearQueue()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.clearQueue(guildID);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'clear-queue'){
|
|
||||||
client.player.clearQueue(message.guild.id);
|
|
||||||
message.channel.send('Queue cleared!');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetQueue
|
|
||||||
|
|
||||||
To get the server queue, use the `client.player.getQueue()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.getQueue(guildID);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'queue'){
|
|
||||||
let queue = await client.player.getQueue(message.guild.id);
|
|
||||||
message.channel.send('Server queue:\n'+(queue.songs.map((song, i) => {
|
|
||||||
return `${i === 0 ? 'Current' : `#${i+1}`} - ${song.name} | ${song.author}`
|
|
||||||
}).join('\n')));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* Server queue:
|
|
||||||
* Current - Despacito | Luis Fonsi
|
|
||||||
* #2 - Memories | Maroon 5
|
|
||||||
* #3 - Dance Monkey | Tones And I
|
|
||||||
* #4 - Circles | Post Malone
|
|
||||||
*/
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Skip
|
|
||||||
|
|
||||||
To skip the current song, use the `client.player.skip()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.skip(guildID);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'skip'){
|
|
||||||
let song = await client.player.skip(message.guild.id);
|
|
||||||
message.channel.send(`${song.name} skipped!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Now Playing
|
|
||||||
|
|
||||||
To get the currently playing song, use the `client.player.nowPlaying()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.nowPlaying(guildID);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'now-playing'){
|
|
||||||
let song = await client.player.nowPlaying(message.guild.id);
|
|
||||||
message.channel.send(`Currently playing ${song.name}...`);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Repeat
|
|
||||||
|
|
||||||
To repeat the current song, use the `client.player.setRepeatMode()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.setRepeatMode(guildID, boolean);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'enable-repeat'){
|
|
||||||
// Enable repeat mode
|
|
||||||
client.player.setRepeatMode(message.guild.id, true);
|
|
||||||
// Get the current song
|
|
||||||
let song = await client.player.nowPlaying(message.guild.id);
|
|
||||||
message.channel.send(`${song.name} will be repeated indefinitely!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(command === 'disable-repeat'){
|
|
||||||
// Disable repeat mode
|
|
||||||
client.player.setRepeatMode(message.guild.id, false);
|
|
||||||
// Get the current song
|
|
||||||
let song = await client.player.nowPlaying(message.guild.id);
|
|
||||||
message.channel.send(`${song.name} will no longer be repeated indefinitely!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remove
|
|
||||||
|
|
||||||
To remove a song from the queue, use the `client.player.remove()` function.
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.player.remove(guildID, song);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', async (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'remove'){
|
|
||||||
// Removes a song from the queue
|
|
||||||
client.player.remove(message.guild.id, args[0]).then(() => {
|
|
||||||
message.channel.send('Removed song!');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Info Messages
|
|
||||||
|
|
||||||
You can send a message when the queue ends or when the song changes:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
if(command === 'play'){
|
|
||||||
let song = await client.player.play(message.member.voice.channel, args[0]);
|
|
||||||
song.queue.on('end', () => {
|
|
||||||
message.channel.send('The queue is empty, please add new songs!');
|
|
||||||
});
|
|
||||||
song.queue.on('songChanged', (oldSong, newSong, skipped, repeatMode) => {
|
|
||||||
if(repeatMode){
|
|
||||||
message.channel.send(`Playing ${newSong} again...`);
|
|
||||||
} else {
|
|
||||||
message.channel.send(`Now playing ${newSong}...`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Handle errors
|
|
||||||
|
|
||||||
There are 2 main errors that you can handle like this:
|
|
||||||
|
|
||||||
```js
|
|
||||||
client.on('message', (message) => {
|
|
||||||
|
|
||||||
const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
|
||||||
const command = args.shift().toLowerCase();
|
|
||||||
|
|
||||||
// Error 1:
|
|
||||||
// Song not found
|
|
||||||
if(command === 'play'){
|
|
||||||
client.player.play(message.member.voice.channel, args[0]).then((song) => {
|
|
||||||
message.channel.send(`Currently playing ${song.name}!`);
|
|
||||||
}).catch(() => {
|
|
||||||
message.channel.send(`No song found for ${args[0]}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error 2:
|
|
||||||
// Not playing
|
|
||||||
if(command === 'queue'){
|
|
||||||
let playing = client.player.isPlaying(message.guild.id);
|
|
||||||
if(!playing) return message.channel.send(':x: No songs currently playing!');
|
|
||||||
// you are sure it works:
|
|
||||||
client.player.getQueue(message.guild.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
17
package.json
17
package.json
|
@ -28,13 +28,22 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Androz2091/discord-player#readme",
|
"homepage": "https://github.com/Androz2091/discord-player#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ffmpeg-static": "^4.1.1",
|
"discord-ytdl-core": "^4.0.2",
|
||||||
"merge-options": "^2.0.0",
|
"merge-options": "^2.0.0",
|
||||||
"simple-youtube-api": "^5.2.1",
|
"node-fetch": "^2.6.0",
|
||||||
"ytdl-core": "^3.0.0"
|
"spotify-url-info": "^1.3.1",
|
||||||
|
"ytpl": "^0.1.22",
|
||||||
|
"ytsr": "^0.1.15"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"discord.js": "discordjs/discord.js",
|
"@discordjs/opus": "^0.3.2",
|
||||||
|
"discord.js": "^12.2.0",
|
||||||
|
"eslint": "^7.1.0",
|
||||||
|
"eslint-config-standard": "^14.1.1",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
"jsdoc": "^3.6.3",
|
"jsdoc": "^3.6.3",
|
||||||
"minami": "Androz2091/minami"
|
"minami": "Androz2091/minami"
|
||||||
}
|
}
|
||||||
|
|
1074
src/Player.js
1074
src/Player.js
File diff suppressed because it is too large
Load diff
129
src/Queue.js
129
src/Queue.js
|
@ -1,68 +1,101 @@
|
||||||
const { EventEmitter } = require('events');
|
const Discord = require('discord.js')
|
||||||
|
const { EventEmitter } = require('events')
|
||||||
|
const Track = require('./Track')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a guild queue.
|
* Represents a guild queue.
|
||||||
*/
|
*/
|
||||||
class Queue extends EventEmitter {
|
class Queue extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a guild queue.
|
* @param {Discord.Snowflake} guildID ID of the guild this queue is for.
|
||||||
* @param {string} guildID
|
|
||||||
*/
|
*/
|
||||||
constructor(guildID){
|
constructor (guildID) {
|
||||||
super();
|
super()
|
||||||
/**
|
/**
|
||||||
* The guild ID.
|
* ID of the guild this queue is for.
|
||||||
* @type {Snowflake}
|
* @type {Discord.Snowflake}
|
||||||
*/
|
*/
|
||||||
this.guildID = guildID;
|
this.guildID = guildID
|
||||||
/**
|
/**
|
||||||
* The stream dispatcher.
|
* The voice connection of this queue.
|
||||||
* @type {StreamDispatcher}
|
* @type {Discord.VoiceConnection}
|
||||||
*/
|
*/
|
||||||
this.dispatcher = null;
|
this.voiceConnection = null
|
||||||
/**
|
/**
|
||||||
* The voice connection.
|
* The song currently played.
|
||||||
* @type {VoiceConnection}
|
* @type {Track}
|
||||||
*/
|
*/
|
||||||
this.connection = null;
|
this.playing = null
|
||||||
/**
|
/**
|
||||||
* Songs. The first one is currently playing and the rest is going to be played.
|
* The tracks of this queue. The first one is currenlty playing and the others are going to be played.
|
||||||
* @type {Song[]}
|
* @type {Track[]}
|
||||||
*/
|
*/
|
||||||
this.songs = [];
|
this.tracks = []
|
||||||
/**
|
/**
|
||||||
* Whether the stream is currently stopped.
|
* Whether the stream is currently stopped.
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.stopped = false;
|
this.stopped = false
|
||||||
/**
|
/**
|
||||||
* Whether the last song was skipped.
|
* Whether the last track was skipped.
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.skipped = false;
|
this.lastSkipped = false
|
||||||
/**
|
/**
|
||||||
* The stream volume.
|
* The stream volume of this queue. (0-100)
|
||||||
* @type {Number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.volume = 100;
|
this.volume = 100
|
||||||
/**
|
/**
|
||||||
* Whether the stream is currently playing.
|
* Whether the stream is currently paused.
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.playing = true;
|
this.paused = true
|
||||||
/**
|
/**
|
||||||
* Whether the repeat mode is enabled.
|
* Whether the repeat mode is enabled.
|
||||||
* @type {Boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.repeatMode = false;
|
this.repeatMode = false
|
||||||
|
/**
|
||||||
|
* Filters status
|
||||||
|
* @type {Filters}
|
||||||
|
*/
|
||||||
|
this.filters = {}
|
||||||
|
/**
|
||||||
|
* Additional stream time
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
this.additionalStreamTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
get calculatedVolume () {
|
||||||
|
return this.filters.bassboost ? this.volume + 50 : this.volume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Queue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when the queue is empty.
|
* Emitted when the queue is empty.
|
||||||
* @event Queue#end
|
* @event Queue#end
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* client.on('message', (message) => {
|
||||||
|
*
|
||||||
|
* const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
||||||
|
* const command = args.shift().toLowerCase();
|
||||||
|
*
|
||||||
|
* if(command === 'play'){
|
||||||
|
*
|
||||||
|
* let track = await client.player.play(message.member.voice.channel, args[0]);
|
||||||
|
*
|
||||||
|
* track.queue.on('end', () => {
|
||||||
|
* message.channel.send('The queue is empty, please add new tracks!');
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* });
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,11 +104,31 @@ class Queue extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when the song changes.
|
* Emitted when the track changes.
|
||||||
* @event Queue#songChanged
|
* @event Queue#trackChanged
|
||||||
* @param {Song} oldSong The old song (playing before)
|
* @param {Track} oldTrack The old track (playing before)
|
||||||
* @param {Song} newSong The new song (currently playing)
|
* @param {Track} newTrack The new track (currently playing)
|
||||||
* @param {Boolean} skipped Whether the change is due to the skip() function
|
* @param {Boolean} skipped Whether the change is due to the skip() function
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* client.on('message', (message) => {
|
||||||
|
*
|
||||||
|
* const args = message.content.slice(settings.prefix.length).trim().split(/ +/g);
|
||||||
|
* const command = args.shift().toLowerCase();
|
||||||
|
*
|
||||||
|
* if(command === 'play'){
|
||||||
|
*
|
||||||
|
* let track = await client.player.play(message.member.voice.channel, args[0]);
|
||||||
|
*
|
||||||
|
* track.queue.on('trackChanged', (oldTrack, newTrack, skipped, repeatMode) => {
|
||||||
|
* if(repeatMode){
|
||||||
|
* message.channel.send(`Playing ${newTrack} again...`);
|
||||||
|
* } else {
|
||||||
|
* message.channel.send(`Now playing ${newTrack}...`);
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* });
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = Queue;
|
|
58
src/Song.js
58
src/Song.js
|
@ -1,58 +0,0 @@
|
||||||
/**
|
|
||||||
* Represents a song.
|
|
||||||
*/
|
|
||||||
class Song {
|
|
||||||
/**
|
|
||||||
* @param {Video} video The Youtube video
|
|
||||||
* @param {Queue} queue The queue in which the song is
|
|
||||||
*/
|
|
||||||
constructor(video, queue, requestedBy) {
|
|
||||||
/**
|
|
||||||
* Song name.
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.name = video.title;
|
|
||||||
/**
|
|
||||||
* Song duration.
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
this.duration = ((video.duration.hours*3600)+(video.duration.minutes*60)+(video.duration.seconds)) * 1000;
|
|
||||||
/**
|
|
||||||
* Raw video object from Simple Youtube API
|
|
||||||
* @type {Video}
|
|
||||||
*/
|
|
||||||
this.rawVideo = video;
|
|
||||||
/**
|
|
||||||
* Raw informations about the song.
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
this.raw = video.raw;
|
|
||||||
/**
|
|
||||||
* Author channel of the song.
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.author = video.raw.snippet.channelTitle;
|
|
||||||
/**
|
|
||||||
* Youtube video URL.
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.url = `https://www.youtube.com/watch?v=${video.id}`;
|
|
||||||
/**
|
|
||||||
* Youtube video thumbnail.
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
this.thumbnail = video.raw.snippet.thumbnails.default.url;
|
|
||||||
/**
|
|
||||||
* The queue in which the song is.
|
|
||||||
* @type {Queue}
|
|
||||||
*/
|
|
||||||
this.queue = queue;
|
|
||||||
/**
|
|
||||||
* The user who requested that song.
|
|
||||||
* @type {User}
|
|
||||||
*/
|
|
||||||
this.requestedBy = requestedBy;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Song;
|
|
80
src/Track.js
Normal file
80
src/Track.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
const Discord = require('discord.js')
|
||||||
|
const Queue = require('./Queue')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a track.
|
||||||
|
*/
|
||||||
|
class Track {
|
||||||
|
/**
|
||||||
|
* @param {Object} videoData The video data for this track
|
||||||
|
* @param {Discord.User?} user The user who requested the track
|
||||||
|
* @param {Queue?} queue The queue in which is the track is
|
||||||
|
*/
|
||||||
|
constructor (videoData, user, queue) {
|
||||||
|
/**
|
||||||
|
* The track name
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.name = videoData.title
|
||||||
|
/**
|
||||||
|
* The Youtube URL of the track
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.url = videoData.link
|
||||||
|
/**
|
||||||
|
* The video duration (formatted).
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.duration = videoData.duration
|
||||||
|
/**
|
||||||
|
* The video description
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.description = videoData.description
|
||||||
|
/**
|
||||||
|
* The video thumbnail
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.thumbnail = videoData.thumbnail
|
||||||
|
/**
|
||||||
|
* The video views
|
||||||
|
* @type {?number}
|
||||||
|
*/
|
||||||
|
this.views = videoData.views
|
||||||
|
/**
|
||||||
|
* The video channel
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.author = videoData.author.name
|
||||||
|
/**
|
||||||
|
* The user who requested the track
|
||||||
|
* @type {Discord.User?}
|
||||||
|
*/
|
||||||
|
this.requestedBy = user
|
||||||
|
/**
|
||||||
|
* The queue in which the track is
|
||||||
|
* @type {Queue}
|
||||||
|
*/
|
||||||
|
this.queue = queue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The track duration
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
get durationMS () {
|
||||||
|
const args = this.duration.split(':')
|
||||||
|
if (args.length === 3) {
|
||||||
|
return parseInt(args[0]) * 60 * 60 * 1000 +
|
||||||
|
parseInt(args[1]) * 60 * 1000 +
|
||||||
|
parseInt(args[2]) * 1000
|
||||||
|
} else if (args.length === 2) {
|
||||||
|
return parseInt(args[0]) * 60 * 1000 +
|
||||||
|
parseInt(args[1]) * 1000
|
||||||
|
} else {
|
||||||
|
return parseInt(args[0]) * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Track
|
49
src/Util.js
49
src/Util.js
|
@ -1,49 +0,0 @@
|
||||||
const fetch = require('node-fetch');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utilities.
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
class Util {
|
|
||||||
|
|
||||||
constructor(){}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the first youtube results for your search.
|
|
||||||
* @param {string} search The name of the video or the video URL.
|
|
||||||
* @param {Youtube} SYA The Simple Youtube API Client.
|
|
||||||
* @returns {Promise<Video>}
|
|
||||||
*/
|
|
||||||
static getFirstYoutubeResult(search, SYA){
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
search = search.replace(/<(.+)>/g, "$1");
|
|
||||||
// Try with URL
|
|
||||||
SYA.getVideo(search).then(async (video) => {
|
|
||||||
video = await video.fetch();
|
|
||||||
resolve(video);
|
|
||||||
}).catch(async (err) => {
|
|
||||||
if(err.message === "Bad Request"){
|
|
||||||
reject('Invalid Youtube Data v3 API key.');
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
// Try with song name
|
|
||||||
let results = await SYA.searchVideos(search, 1);
|
|
||||||
if(results.length < 1) return reject('Not found');
|
|
||||||
let fetched = await results.shift().fetch();
|
|
||||||
results.push(fetched);
|
|
||||||
resolve(results.pop());
|
|
||||||
} catch(err){
|
|
||||||
if(err.message === "Bad Request"){
|
|
||||||
reject('Invalid Youtube Data v3 API key.');
|
|
||||||
} else {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Util;
|
|
Loading…
Reference in a new issue