🎨 Clean up code
This commit is contained in:
parent
50158cfe12
commit
f134eb0ca0
4 changed files with 120 additions and 97 deletions
201
src/Player.js
201
src/Player.js
|
@ -124,72 +124,64 @@ class Player extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _searchTracks (message, query) {
|
/**
|
||||||
const tracks = []
|
*
|
||||||
|
* @param {Discord.Message} message
|
||||||
|
* @param {string} query
|
||||||
|
*/
|
||||||
|
_searchTracks (message, query) {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const tracks = []
|
||||||
|
|
||||||
const queryType = this.resolveQueryType(query)
|
const queryType = this.resolveQueryType(query)
|
||||||
|
|
||||||
if (queryType === 'spotify-song') {
|
if (queryType === 'spotify-song') {
|
||||||
const matchSpotifyURL = query.match(/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/)
|
const matchSpotifyURL = query.match(/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/)
|
||||||
if (matchSpotifyURL) {
|
if (matchSpotifyURL) {
|
||||||
const spotifyData = await spotify.getPreview(query).catch(() => {})
|
const spotifyData = await spotify.getPreview(query).catch(() => {})
|
||||||
if (spotifyData) {
|
if (spotifyData) {
|
||||||
const YTQuery = `${spotifyData.artist} - ${spotifyData.track}`
|
const YTQuery = `${spotifyData.artist} - ${spotifyData.track}`
|
||||||
const results = await ytsr(query)
|
const results = await ytsr(YTQuery)
|
||||||
|
|
||||||
|
if (results.items.length !== 0) {
|
||||||
|
const resultsVideo = results.items.filter((i) => i.type === 'video')
|
||||||
|
tracks.push(...resultsVideo.map((r) => new Track(r, message.author, null)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (queryType === 'youtube-video-keywords') {
|
||||||
|
await ytsr(query).then((results) => {
|
||||||
if (results.items.length !== 0) {
|
if (results.items.length !== 0) {
|
||||||
const resultsVideo = results.items.filter((i) => i.type === 'video')
|
const resultsVideo = results.items.filter((i) => i.type === 'video')
|
||||||
tracks.push(...resultsVideo.map((r) => new Track(r, message.author, null)))
|
tracks.push(...resultsVideo.map((r) => new Track(r, message.author, null)))
|
||||||
}
|
}
|
||||||
}
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
} else if (queryType === 'youtube-playlist') {
|
|
||||||
const playlistID = await ytpl.getPlaylistID(query).catch(() => {})
|
|
||||||
if (playlistID) {
|
|
||||||
const playlist = await ytpl(playlistID).catch(() => {})
|
|
||||||
if (playlist) {
|
|
||||||
tracks.push(...playlist.items.map((i) => new Track({
|
|
||||||
title: i.title,
|
|
||||||
duration: i.duration,
|
|
||||||
thumbnail: i.thumbnail,
|
|
||||||
author: i.author,
|
|
||||||
link: i.url,
|
|
||||||
fromPlaylist: true
|
|
||||||
}, message.author, null)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (queryType === 'youtube-video-keywords') {
|
|
||||||
await ytsr(query).then((results) => {
|
|
||||||
if (results.items.length !== 0) {
|
|
||||||
const resultsVideo = results.items.filter((i) => i.type === 'video')
|
|
||||||
tracks.push(...resultsVideo.map((r) => new Track(r, null, null)))
|
|
||||||
}
|
|
||||||
}).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tracks.length === 0) throw new Error('No tracks found for the specified query.')
|
if (tracks.length === 0) throw new Error('No tracks found for the specified query.')
|
||||||
|
|
||||||
let track = tracks[0]
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.emit('searchResults', message, query, tracks)
|
this.emit('searchResults', message, query, tracks)
|
||||||
const answers = await message.channel.awaitMessages(m => m.author.id === message.author.id, {
|
|
||||||
max: 1,
|
const collector = message.channel.createMessageCollector((m) => m.author.id === message.author.id, {
|
||||||
time: 60000,
|
time: 60000,
|
||||||
errors: ['time']
|
errors: ['time']
|
||||||
})
|
})
|
||||||
const index = parseInt(answers.first().content, 10)
|
collector.on('collect', ({ content }) => {
|
||||||
if (isNaN(index) || index > tracks.length || index < 1) {
|
if (!isNaN(content) && parseInt(content) >= 1 && parseInt(content) <= tracks.length) {
|
||||||
this.emit('searchCancel', message)
|
const index = parseInt(content, 10)
|
||||||
return
|
const track = tracks[index - 1]
|
||||||
}
|
collector.stop()
|
||||||
track = tracks[index - 1]
|
resolve(track)
|
||||||
} catch {
|
} else {
|
||||||
this.emit('searchCancel', message)
|
this.emit('searchInvalidResponse', message, query, tracks, content)
|
||||||
return
|
}
|
||||||
}
|
})
|
||||||
|
collector.on('end', (collected, reason) => {
|
||||||
return track
|
if (reason === 'time') {
|
||||||
|
this.emit('searchCancel', message, query, tracks)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilters (message, newFilters) {
|
setFilters (message, newFilters) {
|
||||||
|
@ -211,25 +203,25 @@ class Player extends EventEmitter {
|
||||||
_addTracksToQueue (message, tracks) {
|
_addTracksToQueue (message, tracks) {
|
||||||
const queue = this.getQueue(message)
|
const queue = this.getQueue(message)
|
||||||
if (!queue) throw new Error('Cannot add tracks to queue because no song is currently played on the server.')
|
if (!queue) throw new Error('Cannot add tracks to queue because no song is currently played on the server.')
|
||||||
queue.tracks = queue.tracks.concat(tracks)
|
queue.tracks = queue.tracks.push(...tracks)
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
_createQueue (message, track) {
|
_createQueue (message, track) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const channel = message.member.voice ? message.member.voice.channel : null
|
const channel = message.member.voice ? message.member.voice.channel : null
|
||||||
if (!channel) reject('NotConnected')
|
if (!channel) reject(new Error('NotConnected'))
|
||||||
const queue = new Queue(message.guild.id, message, this.filters)
|
const queue = new Queue(message.guild.id, message, this.filters)
|
||||||
this.queues.set(message.guild.id, queue)
|
this.queues.set(message.guild.id, queue)
|
||||||
channel.join().then((connection) => {
|
channel.join().then((connection) => {
|
||||||
queue.voiceConnection = connection
|
queue.voiceConnection = connection
|
||||||
queue.tracks.push(track)
|
queue.tracks.push(track)
|
||||||
this.emit('queueCreate', message, queue)
|
this.emit('queueCreate', message, queue)
|
||||||
this._playTrack(message.guild.id, true)
|
this._playTrack(queue, true)
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
this.queues.delete(message.guild.id)
|
this.queues.delete(message.guild.id)
|
||||||
reject('UnableToJoin')
|
reject(new Error('UnableToJoin'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -256,7 +248,7 @@ class Player extends EventEmitter {
|
||||||
const isPlaying = this.isPlaying(message)
|
const isPlaying = this.isPlaying(message)
|
||||||
if (!isPlaying) {
|
if (!isPlaying) {
|
||||||
if (this.util.isYTPlaylistLink(query)) {
|
if (this.util.isYTPlaylistLink(query)) {
|
||||||
this._handlePlaylist(message, query)
|
return this._handlePlaylist(message, query)
|
||||||
}
|
}
|
||||||
let trackToPlay
|
let trackToPlay
|
||||||
if (query instanceof Track) {
|
if (query instanceof Track) {
|
||||||
|
@ -322,10 +314,10 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setVolume (guildID, percent) {
|
setVolume (message, percent) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Get guild queue
|
// Get guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
// Update volume
|
// Update volume
|
||||||
queue.volume = percent
|
queue.volume = percent
|
||||||
|
@ -337,14 +329,14 @@ class Player extends EventEmitter {
|
||||||
|
|
||||||
getQueue (message) {
|
getQueue (message) {
|
||||||
// Gets guild queue
|
// Gets guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === message.guild.id)
|
const queue = this.queues.get(message.guild.id)
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
clearQueue (guildID) {
|
clearQueue (message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Get guild queue
|
// Get guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
// Clear queue
|
// Clear queue
|
||||||
queue.tracks = []
|
queue.tracks = []
|
||||||
|
@ -353,10 +345,10 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
skip (guildID) {
|
skip (message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Get guild queue
|
// Get guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
const currentTrack = queue.playing
|
const currentTrack = queue.playing
|
||||||
// End the dispatcher
|
// End the dispatcher
|
||||||
|
@ -367,10 +359,10 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
nowPlaying (guildID) {
|
nowPlaying (message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Get guild queue
|
// Get guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
const currentTrack = queue.tracks[0]
|
const currentTrack = queue.tracks[0]
|
||||||
// Resolve the current track
|
// Resolve the current track
|
||||||
|
@ -378,10 +370,10 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setRepeatMode (guildID, enabled) {
|
setRepeatMode (message, enabled) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Get guild queue
|
// Get guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
// Enable/Disable repeat mode
|
// Enable/Disable repeat mode
|
||||||
queue.repeatMode = enabled
|
queue.repeatMode = enabled
|
||||||
|
@ -390,10 +382,10 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
shuffle (guildID) {
|
shuffle (message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Get guild queue
|
// Get guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
// Shuffle the queue (except the first track)
|
// Shuffle the queue (except the first track)
|
||||||
const currentTrack = queue.tracks.shift()
|
const currentTrack = queue.tracks.shift()
|
||||||
|
@ -404,10 +396,10 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
remove (guildID, track) {
|
remove (message, track) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Gets guild queue
|
// Gets guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return reject(new Error('Not playing'))
|
if (!queue) return reject(new Error('Not playing'))
|
||||||
// Remove the track from the queue
|
// Remove the track from the queue
|
||||||
let trackFound = null
|
let trackFound = null
|
||||||
|
@ -427,9 +419,9 @@ class Player extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
createProgressBar (guildID) {
|
createProgressBar (message) {
|
||||||
// Gets guild queue
|
// Gets guild queue
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
const queue = this.queues.get(message.guild.id)
|
||||||
if (!queue) return
|
if (!queue) return
|
||||||
// Stream time of the dispatcher
|
// Stream time of the dispatcher
|
||||||
const currentStreamTime = queue.voiceConnection.dispatcher
|
const currentStreamTime = queue.voiceConnection.dispatcher
|
||||||
|
@ -449,25 +441,40 @@ class Player extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle voiceStateUpdate event.
|
||||||
|
* @param {Discord.VoiceState} oldState
|
||||||
|
* @param {Discord.VoiceState} newState
|
||||||
|
*/
|
||||||
_handleVoiceStateUpdate (oldState, newState) {
|
_handleVoiceStateUpdate (oldState, newState) {
|
||||||
const isEmpty = (channel) => (channel.members.filter((member) => !member.user.bot)).size === 0
|
// Search for a queue for this channel
|
||||||
|
const queue = this.queues.find((g) => g.guildID === oldState.guild.id)
|
||||||
|
if (!queue) return
|
||||||
|
|
||||||
|
// if the bot has been kicked from the channel, destroy ytdl stream and remove the queue
|
||||||
|
if (newState.member.id === this.client.user.id && !newState.channelID) {
|
||||||
|
queue.stream.destroy()
|
||||||
|
this.queues.delete(newState.guild.id)
|
||||||
|
this.emit('botDisconnected')
|
||||||
|
}
|
||||||
|
|
||||||
|
// process leaveOnEmpty checks
|
||||||
if (!this.options.leaveOnEmpty) return
|
if (!this.options.leaveOnEmpty) return
|
||||||
// If the member leaves a voice channel
|
// If the member leaves a voice channel
|
||||||
if (!oldState.channelID || newState.channelID) return
|
if (!oldState.channelID || newState.channelID) return
|
||||||
// Search for a queue for this channel
|
|
||||||
const queue = this.queues.find((g) => g.voiceConnection.channel.id === oldState.channelID)
|
// If the channel is not empty
|
||||||
if (queue) {
|
if (!this.util.isVoiceEmpty(queue.voiceConnection.channel)) return
|
||||||
// If the channel is not empty
|
setTimeout(() => {
|
||||||
if (!isEmpty(queue.voiceConnection.channel)) return
|
if (!this.util.isVoiceEmpty(queue.voiceConnection.channel)) return
|
||||||
setTimeout(() => {
|
if (!this.queues.has(queue.guildID)) return
|
||||||
// Disconnect from the voice channel
|
// Disconnect from the voice channel
|
||||||
queue.voiceConnection.channel.leave()
|
queue.voiceConnection.channel.leave()
|
||||||
// Delete the queue
|
// Delete the queue
|
||||||
this.queues.delete(queue.guildID)
|
this.queues.delete(queue.guildID)
|
||||||
// Emit end event
|
// Emit end event
|
||||||
queue.emit('channelEmpty', queue.firstMessage, queue)
|
queue.emit('channelEmpty', queue.firstMessage, queue)
|
||||||
}, this.options.leaveOnEmptyCooldown ?? 0)
|
}, this.options.leaveOnEmptyCooldown ?? 0)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_playYTDLStream (track, queue, updateFilter) {
|
_playYTDLStream (track, queue, updateFilter) {
|
||||||
|
@ -512,22 +519,28 @@ class Player extends EventEmitter {
|
||||||
// Reset streamTime
|
// Reset streamTime
|
||||||
queue.additionalStreamTime = 0
|
queue.additionalStreamTime = 0
|
||||||
// Play the next track
|
// Play the next track
|
||||||
return this._playTrack(queue.guildID, false)
|
return this._playTrack(queue, false)
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async _playTrack (guildID, firstPlay) {
|
/**
|
||||||
// Get guild queue
|
*
|
||||||
const queue = this.queues.find((g) => g.guildID === guildID)
|
* @param {Queue} queue The queue to play.
|
||||||
|
* @param {*} firstPlay
|
||||||
|
*/
|
||||||
|
async _playTrack (queue, firstPlay) {
|
||||||
|
if (this.options.leaveOnEmpty && this.util.isVoiceEmpty(queue.voiceConnection.channel)) {
|
||||||
|
|
||||||
|
}
|
||||||
if (queue.stopped) return
|
if (queue.stopped) return
|
||||||
// If there isn't any music in the queue
|
// If there isn't any music in the queue
|
||||||
if (queue.tracks.length === 0) {
|
if (queue.tracks.length === 0) {
|
||||||
// Leave the voice channel
|
// Leave the voice channel
|
||||||
if (this.options.leaveOnEnd && !queue.stopped) queue.voiceConnection.channel.leave()
|
if (this.options.leaveOnEnd && !queue.stopped) queue.voiceConnection.channel.leave()
|
||||||
// Remove the guild from the guilds list
|
// Remove the guild from the guilds list
|
||||||
this.queues.delete(guildID)
|
this.queues.delete(queue.guildID)
|
||||||
// Emit stop event
|
// Emit stop event
|
||||||
if (queue.stopped) {
|
if (queue.stopped) {
|
||||||
return queue.emit('stop')
|
return queue.emit('stop')
|
||||||
|
|
|
@ -2,6 +2,7 @@ const Discord = require('discord.js')
|
||||||
const { EventEmitter } = require('events')
|
const { EventEmitter } = require('events')
|
||||||
const Track = require('./Track')
|
const Track = require('./Track')
|
||||||
const Player = require('./Player')
|
const Player = require('./Player')
|
||||||
|
const { Stream } = require('stream')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a guild queue.
|
* Represents a guild queue.
|
||||||
|
@ -24,6 +25,11 @@ class Queue extends EventEmitter {
|
||||||
* @type {Discord.VoiceConnection}
|
* @type {Discord.VoiceConnection}
|
||||||
*/
|
*/
|
||||||
this.voiceConnection = null
|
this.voiceConnection = null
|
||||||
|
/**
|
||||||
|
* The ytdl stream.
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
this.stream = null
|
||||||
/**
|
/**
|
||||||
* The tracks of this queue. The first one is currenlty playing and the others are going to be played.
|
* The tracks of this queue. The first one is currenlty playing and the others are going to be played.
|
||||||
* @type {Track[]}
|
* @type {Track[]}
|
||||||
|
|
|
@ -12,15 +12,15 @@ class Track {
|
||||||
*/
|
*/
|
||||||
constructor (videoData, user, queue) {
|
constructor (videoData, user, queue) {
|
||||||
/**
|
/**
|
||||||
* The track name
|
* The track title
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
this.name = videoData.title
|
this.title = videoData.title
|
||||||
/**
|
/**
|
||||||
* The Youtube URL of the track
|
* The Youtube URL of the track
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
this.url = videoData.link
|
this.url = videoData.link ?? videoData.url
|
||||||
/**
|
/**
|
||||||
* The video duration (formatted).
|
* The video duration (formatted).
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
|
@ -9,6 +9,10 @@ module.exports = class Util {
|
||||||
throw new Error(`The ${this.constructor.name} class may not be instantiated.`)
|
throw new Error(`The ${this.constructor.name} class may not be instantiated.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isVoiceEmpty (channel) {
|
||||||
|
return channel.members.filter((member) => !member.user.bot).size === 0
|
||||||
|
}
|
||||||
|
|
||||||
static isSpotifyLink (query) {
|
static isSpotifyLink (query) {
|
||||||
return spotifySongRegex.test(query)
|
return spotifySongRegex.test(query)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue