2020-01-12 00:01:18 +05:00
|
|
|
const ytdl = require('ytdl-core');
|
2020-06-02 16:11:12 +05:00
|
|
|
const SimpleYouTubeAPI = require('simple-youtube-api');
|
|
|
|
const Discord = require('discord.js');
|
2020-01-12 00:01:18 +05:00
|
|
|
|
|
|
|
const Queue = require('./Queue');
|
2020-06-02 16:11:12 +05:00
|
|
|
const Track = require('./Track');
|
2020-01-12 00:01:18 +05:00
|
|
|
const Util = require('./Util');
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* @typedef PlayerOptions
|
|
|
|
* @property {boolean} [leaveOnEnd=true] Whether the bot should leave the current voice channel when the queue ends.
|
|
|
|
* @property {boolean} [leaveOnStop=true] Whether the bot should leave the current voice channel when the stop() function is used.
|
|
|
|
* @property {boolean} [leaveOnEmpty=true] Whether the bot should leave the voice channel if there is no more member in it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default options for the player
|
|
|
|
* @ignore
|
|
|
|
* @type {PlayerOptions}
|
|
|
|
*/
|
|
|
|
const defaultPlayerOptions = {
|
2020-01-12 00:01:18 +05:00
|
|
|
leaveOnEnd: true,
|
2020-01-12 22:55:37 +05:00
|
|
|
leaveOnStop: true,
|
|
|
|
leaveOnEmpty: true
|
2020-01-12 00:01:18 +05:00
|
|
|
};
|
|
|
|
|
|
|
|
class Player {
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* @param {Discord.Client} client Discord.js client
|
|
|
|
* @param {string} youtubeToken Youtube Data v3 API Key
|
|
|
|
* @param {PlayerOptions} options Player options
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
|
|
|
constructor(client, youtubeToken, options = {}){
|
|
|
|
if(!client) throw new SyntaxError('Invalid Discord client');
|
|
|
|
if(!youtubeToken) throw new SyntaxError('Invalid Token: Token must be a String');
|
2020-06-02 16:11:12 +05:00
|
|
|
|
2020-01-12 00:01:18 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Discord.js client instance
|
|
|
|
* @type {Discord.Client}
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
|
|
|
this.client = client;
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* YouTube API Key
|
2020-01-12 00:01:18 +05:00
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
this.youtubeToken = youtubeToken;
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Simple YouTube API client instance
|
|
|
|
* @type {SimpleYouTubeAPI.YouTube}
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
this.youtube = new SimpleYouTubeAPI.YouTube(this.youtubeToken);
|
2020-01-12 00:01:18 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Player queues
|
2020-01-12 00:01:18 +05:00
|
|
|
* @type {Queue[]}
|
|
|
|
*/
|
|
|
|
this.queues = [];
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Player options
|
2020-01-12 00:01:18 +05:00
|
|
|
* @type {PlayerOptions}
|
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
this.options = defaultPlayerOptions;
|
|
|
|
for(const prop in options){
|
|
|
|
this.options[prop] = options[prop];
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Utilities methods for the player
|
|
|
|
* @type {Util}
|
|
|
|
*/
|
|
|
|
this.util = new Util(this.youtube)
|
2020-01-12 22:55:37 +05:00
|
|
|
|
|
|
|
// Listener to check if the channel is empty
|
2020-06-02 16:11:12 +05:00
|
|
|
client.on('voiceStateUpdate', (oldState, newState) => this._handleVoiceStateUpdate.call(this, oldState, newState));
|
2020-01-12 00:01:18 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Whether a guild is currently playing something
|
|
|
|
* @param {Discord.Snowflake} guildID The guild ID to check
|
|
|
|
* @returns {boolean} Whether the guild is currently playing tracks
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
|
|
|
isPlaying(guildID) {
|
|
|
|
return this.queues.some((g) => g.guildID === guildID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Play a track in a voice channel
|
|
|
|
* @param {Discord.VoiceChannel} voiceChannel The voice channel in which the track will be played
|
|
|
|
* @param {Track|string} track The name of the track to play
|
|
|
|
* @param {Discord.User?} user The user who requested the track
|
|
|
|
* @returns {Promise<Track>} The played track
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
play(voiceChannel, track, user) {
|
2020-01-12 00:01:18 +05:00
|
|
|
this.queues = this.queues.filter((g) => g.guildID !== voiceChannel.id);
|
|
|
|
return new Promise(async (resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
if(!voiceChannel || typeof voiceChannel !== "object"){
|
|
|
|
return reject(`voiceChannel must be type of VoiceChannel. value=${voiceChannel}`);
|
|
|
|
}
|
|
|
|
const connection = voiceChannel.client.voice.connections.find((c) => c.channel.id === voiceChannel.id) || await voiceChannel.join();
|
|
|
|
if(typeof track !== "object"){
|
|
|
|
const results = await this.util.search(track, user);
|
|
|
|
track = results[0];
|
|
|
|
}
|
|
|
|
// Create a new guild with data
|
2020-01-12 00:01:18 +05:00
|
|
|
let queue = new Queue(voiceChannel.guild.id);
|
2020-06-02 16:11:12 +05:00
|
|
|
queue.voiceConnection = connection;
|
|
|
|
// Add the track to the queue
|
|
|
|
track.requestedBy = user;
|
|
|
|
queue.tracks.push(track);
|
2020-01-12 00:01:18 +05:00
|
|
|
// Add the queue to the list
|
|
|
|
this.queues.push(queue);
|
2020-06-02 16:11:12 +05:00
|
|
|
// Play the track
|
|
|
|
this._playTrack(queue.guildID, true);
|
|
|
|
// Resolve the track
|
|
|
|
resolve(track);
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Pause the current track
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the current track should be paused
|
|
|
|
* @returns {Promise<Track>} The paused track
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
pause (guildID) {
|
2020-01-12 00:01:18 +05:00
|
|
|
return new Promise(async(resolve, reject) => {
|
|
|
|
// Gets guild queue
|
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-01-12 00:01:18 +05:00
|
|
|
// Pauses the dispatcher
|
2020-06-02 16:11:12 +05:00
|
|
|
queue.voiceConnection.dispatcher.pause();
|
2020-01-12 00:01:18 +05:00
|
|
|
queue.playing = false;
|
|
|
|
// Resolves the guild queue
|
2020-06-02 16:11:12 +05:00
|
|
|
resolve(queue.tracks[0]);
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Resume the current track
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the current track should be resumed
|
|
|
|
* @returns {Promise<Track>} The resumed track
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
resume (guildID) {
|
2020-01-12 00:01:18 +05:00
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-12 00:01:18 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Pause the dispatcher
|
|
|
|
queue.voiceConnection.dispatcher.resume();
|
2020-01-12 00:01:18 +05:00
|
|
|
queue.playing = true;
|
2020-06-02 16:11:12 +05:00
|
|
|
// Resolve the guild queue
|
|
|
|
resolve(queue.tracks[0]);
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stops playing music.
|
2020-06-02 16:11:12 +05:00
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the music should be stopped
|
2020-01-12 00:01:18 +05:00
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
stop(guildID){
|
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-12 00:01:18 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Stop the dispatcher
|
2020-01-12 00:01:18 +05:00
|
|
|
queue.stopped = true;
|
2020-06-02 16:11:12 +05:00
|
|
|
queue.tracks = [];
|
|
|
|
queue.voiceConnection.dispatcher.end();
|
|
|
|
// Resolve
|
2020-01-12 00:01:18 +05:00
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Update the volume
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the music should be modified
|
|
|
|
* @param {number} percent The new volume (0-100)
|
2020-01-12 00:01:18 +05:00
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
setVolume(guildID, percent) {
|
|
|
|
return new Promise(async(resolve, reject) => {
|
|
|
|
// Gets guild queue
|
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-01-12 00:01:18 +05:00
|
|
|
// Updates volume
|
2020-06-02 16:11:12 +05:00
|
|
|
queue.voiceConnection.dispatcher.setVolumeLogarithmic(percent / 200);
|
|
|
|
queue.volume = percent;
|
2020-01-12 00:01:18 +05:00
|
|
|
// Resolves guild queue
|
2020-06-02 16:11:12 +05:00
|
|
|
resolve();
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Get a guild queue
|
|
|
|
* @param {Discord.Snowflake} guildID
|
2020-01-18 22:29:53 +05:00
|
|
|
* @returns {?Queue}
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
|
|
|
getQueue(guildID) {
|
2020-01-18 22:29:53 +05:00
|
|
|
// Gets guild queue
|
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
|
|
|
return queue;
|
2020-01-12 00:01:18 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Add a track to the guild queue
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the track should be added
|
|
|
|
* @param {string} trackName The name of the track to add to the queue
|
|
|
|
* @param {Discord.User?} requestedBy The user who requested the track
|
|
|
|
* @returns {Promise<Track>} The added track
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
addToQueue(guildID, trackName, requestedBy){
|
2020-01-12 00:01:18 +05:00
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-12 00:01:18 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Search the track
|
|
|
|
let track = await this.util.search(trackName, requestedBy).catch(() => {});
|
|
|
|
if(!track[0]) return reject('Track not found');
|
|
|
|
// Update queue
|
|
|
|
queue.tracks.push(track[0]);
|
|
|
|
// Resolve the track
|
|
|
|
resolve(track[0]);
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-18 17:04:05 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Set the queue for a guild.
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the queue should be set
|
|
|
|
* @param {Track[]} tracks The tracks list
|
|
|
|
* @returns {Promise<Queue>} The new queue
|
2020-01-18 17:04:05 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
setQueue(guildID, tracks){
|
2020-01-18 17:04:05 +05:00
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-18 17:04:05 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Update queue
|
|
|
|
queue.tracks = tracks;
|
|
|
|
// Resolve the queue
|
2020-01-18 17:04:05 +05:00
|
|
|
resolve(queue);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-12 00:01:18 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Clear the guild queue, but not the current track
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the queue should be cleared
|
|
|
|
* @returns {Promise<Queue>} The updated queue
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
|
|
|
clearQueue(guildID){
|
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-12 00:01:18 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Clear queue
|
|
|
|
let currentlyPlaying = queue.tracks.shift();
|
|
|
|
queue.tracks = [ currentlyPlaying ];
|
|
|
|
// Resolve guild queue
|
2020-01-12 00:01:18 +05:00
|
|
|
resolve(queue);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Skip a track
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the track should be skipped
|
|
|
|
* @returns {Promise<Track>}
|
2020-01-12 00:01:18 +05:00
|
|
|
*/
|
|
|
|
skip(guildID){
|
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-12 00:01:18 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
let currentTrack = queue.tracks[0];
|
|
|
|
// End the dispatcher
|
|
|
|
queue.voiceConnection.dispatcher.end();
|
|
|
|
queue.lastSkipped = true;
|
|
|
|
// Resolve the current track
|
|
|
|
resolve(currentTrack);
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-18 15:15:10 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Get the currently playing track
|
|
|
|
* @param {Discord.Snowflake} guildID
|
|
|
|
* @returns {Promise<Track>} The track which is currently played
|
2020-01-18 15:15:10 +05:00
|
|
|
*/
|
|
|
|
nowPlaying(guildID){
|
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-18 15:15:10 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
let currentTrack = queue.tracks[0];
|
|
|
|
// Resolve the current track
|
|
|
|
resolve(currentTrack);
|
2020-01-18 15:15:10 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-18 15:03:22 +05:00
|
|
|
/**
|
|
|
|
* Enable or disable the repeat mode
|
2020-06-02 16:11:12 +05:00
|
|
|
* @param {Discord.Snowflake} guildID
|
2020-01-18 15:03:22 +05:00
|
|
|
* @param {Boolean} enabled Whether the repeat mode should be enabled
|
|
|
|
* @returns {Promise<Void>}
|
|
|
|
*/
|
2020-02-02 15:20:46 +05:00
|
|
|
setRepeatMode(guildID, enabled) {
|
2020-01-18 15:03:22 +05:00
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-01-18 15:03:22 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
2020-02-21 15:32:16 +05:00
|
|
|
if(!queue) return reject('Not playing');
|
2020-01-18 15:03:22 +05:00
|
|
|
// Enable/Disable repeat mode
|
|
|
|
queue.repeatMode = enabled;
|
|
|
|
// Resolve
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-04-24 20:14:34 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Shuffle the guild queue (except the first track)
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the queue should be shuffled
|
|
|
|
* @returns {Promise<Queue>} The updated queue
|
2020-04-24 20:14:34 +05:00
|
|
|
*/
|
|
|
|
shuffle(guildID){
|
|
|
|
return new Promise(async(resolve, reject) => {
|
2020-06-02 16:11:12 +05:00
|
|
|
// Get guild queue
|
2020-04-24 20:14:34 +05:00
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Shuffle the queue (except the first track)
|
|
|
|
let currentTrack = queue.tracks.shift();
|
|
|
|
queue.tracks = queue.tracks.sort(() => Math.random() - 0.5);
|
|
|
|
queue.tracks.unshift(currentTrack);
|
2020-04-24 20:14:34 +05:00
|
|
|
// Resolve
|
2020-06-02 16:11:12 +05:00
|
|
|
resolve(queue);
|
2020-04-24 20:14:34 +05:00
|
|
|
});
|
|
|
|
}
|
2020-04-24 20:15:06 +05:00
|
|
|
|
2020-04-24 20:20:48 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Remove a track from the queue
|
|
|
|
* @param {Discord.Snowflake} guildID The ID of the guild where the track should be removed
|
|
|
|
* @param {number|Track} track The index of the track to remove or the track to remove object
|
|
|
|
* @returns {Promise<Track|null>}
|
2020-04-24 20:20:48 +05:00
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
remove(guildID, track){
|
2020-04-24 20:20:48 +05:00
|
|
|
return new Promise(async(resolve, reject) => {
|
|
|
|
// Gets guild queue
|
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
|
|
|
if(!queue) return reject('Not playing');
|
2020-06-02 16:11:12 +05:00
|
|
|
// Remove the track from the queue
|
|
|
|
let trackFound = null;
|
|
|
|
if(typeof track === "number"){
|
|
|
|
trackFound = queue.tracks[track];
|
|
|
|
if(trackFound){
|
|
|
|
queue.tracks = queue.tracks.filter((s) => s !== trackFound);
|
2020-04-24 20:20:48 +05:00
|
|
|
}
|
|
|
|
} else {
|
2020-06-02 16:11:12 +05:00
|
|
|
trackFound = queue.tracks.find((s) => s === track);
|
|
|
|
if(trackFound){
|
|
|
|
queue.tracks = queue.tracks.filter((s) => s !== trackFound);
|
2020-04-24 20:20:48 +05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Resolve
|
2020-06-02 16:11:12 +05:00
|
|
|
resolve(trackFound);
|
2020-04-24 20:20:48 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-12 00:01:18 +05:00
|
|
|
/**
|
2020-06-02 16:11:12 +05:00
|
|
|
* Handle the voice state update event
|
|
|
|
* @ignore
|
|
|
|
* @private
|
|
|
|
* @param {Discord.VoiceState} oldState
|
|
|
|
* @param {Discord.VoiceState} newState
|
|
|
|
*/
|
|
|
|
_handleVoiceStateUpdate(oldState, newState) {
|
|
|
|
if(!this.options.leaveOnEmpty) return;
|
|
|
|
// If the member leaves a voice channel
|
|
|
|
if(!oldState.channelID || newState.channelID) return;
|
|
|
|
// Search for a queue for this channel
|
|
|
|
let queue = this.queues.find((g) => g.voiceConnection.channel.id === oldState.channelID);
|
|
|
|
if(queue){
|
|
|
|
// If the channel is not empty
|
|
|
|
if(queue.voiceConnection.channel.members.size > 1) return;
|
|
|
|
// Disconnect from the voice channel
|
|
|
|
queue.voiceConnection.channel.leave();
|
|
|
|
// Delete the queue
|
|
|
|
this.queues = this.queues.filter((g) => g.guildID !== queue.guildID);
|
|
|
|
// Emit end event
|
|
|
|
queue.emit('channelEmpty');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start playing a track in a guild
|
2020-01-12 00:01:18 +05:00
|
|
|
* @ignore
|
2020-06-02 16:11:12 +05:00
|
|
|
* @private
|
|
|
|
* @param {Discord.Snowflake} guildID
|
2020-01-12 00:01:18 +05:00
|
|
|
* @param {Boolean} firstPlay Whether the function was called from the play() one
|
|
|
|
*/
|
2020-06-02 16:11:12 +05:00
|
|
|
async _playTrack(guildID, firstPlay) {
|
2020-01-12 00:01:18 +05:00
|
|
|
// Gets guild queue
|
|
|
|
let queue = this.queues.find((g) => g.guildID === guildID);
|
|
|
|
// If there isn't any music in the queue
|
2020-06-02 16:11:12 +05:00
|
|
|
if(queue.tracks.length < 2 && !firstPlay && !queue.repeatMode){
|
2020-01-12 00:01:18 +05:00
|
|
|
// Leaves the voice channel
|
2020-06-02 16:11:12 +05:00
|
|
|
if(this.options.leaveOnEnd && !queue.stopped) queue.voiceConnection.channel.leave();
|
2020-01-12 00:01:18 +05:00
|
|
|
// Remoces the guild from the guilds list
|
|
|
|
this.queues = this.queues.filter((g) => g.guildID !== guildID);
|
|
|
|
// Emits stop event
|
2020-01-12 22:56:11 +05:00
|
|
|
if(queue.stopped){
|
2020-06-02 16:11:12 +05:00
|
|
|
if(this.options.leaveOnStop) queue.voiceConnection.channel.leave();
|
2020-01-12 22:56:11 +05:00
|
|
|
return queue.emit('stop');
|
|
|
|
}
|
2020-01-12 00:01:18 +05:00
|
|
|
// Emits end event
|
|
|
|
return queue.emit('end');
|
|
|
|
}
|
2020-06-02 16:11:12 +05:00
|
|
|
// Emit trackChanged event
|
|
|
|
if(!firstPlay) queue.emit('trackChanged', (!queue.repeatMode ? queue.tracks.shift() : queue.tracks[0]), queue.tracks[0], queue.lastSkipped, queue.repeatMode);
|
|
|
|
queue.lastSkipped = false;
|
|
|
|
let track = queue.tracks[0];
|
|
|
|
// Download the track
|
|
|
|
queue.voiceConnection.play(ytdl(track.url, {
|
|
|
|
filter: "audioonly"
|
|
|
|
}));
|
2020-01-12 00:01:18 +05:00
|
|
|
// Set volume
|
2020-06-02 16:11:12 +05:00
|
|
|
queue.voiceConnection.dispatcher.setVolumeLogarithmic(queue.volume / 200);
|
|
|
|
// When the track ends
|
|
|
|
queue.voiceConnection.dispatcher.on('finish', () => {
|
|
|
|
// Play the next track
|
|
|
|
return this._playTrack(guildID, false);
|
2020-01-12 00:01:18 +05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = Player;
|