🎨 Clean up code

This commit is contained in:
Androz2091 2020-08-13 23:07:54 +02:00
parent 50158cfe12
commit f134eb0ca0
4 changed files with 120 additions and 97 deletions

View file

@ -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')

View file

@ -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[]}

View file

@ -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}

View file

@ -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)
} }