Spotify support & bug fixes (#40)
Co-authored-by: Androz2091 <androz2091@gmail.com>
This commit is contained in:
parent
5b6d1c496b
commit
a0cdcbc461
4 changed files with 152 additions and 139 deletions
|
@ -28,16 +28,16 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Androz2091/discord-player#readme",
|
"homepage": "https://github.com/Androz2091/discord-player#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/opus": "^0.3.2",
|
|
||||||
"discord-ytdl-core": "^4.0.2",
|
"discord-ytdl-core": "^4.0.2",
|
||||||
"merge-options": "^2.0.0",
|
"merge-options": "^2.0.0",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
"simple-youtube-api": "^5.2.1",
|
"spotify-url-info": "^1.3.1",
|
||||||
"ytpl": "^0.1.21",
|
"ytpl": "^0.1.22",
|
||||||
"ytsr": "^0.1.15"
|
"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": "^7.1.0",
|
||||||
"eslint-config-standard": "^14.1.1",
|
"eslint-config-standard": "^14.1.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
|
|
@ -2,7 +2,7 @@ const ytdl = require('discord-ytdl-core')
|
||||||
const Discord = require('discord.js')
|
const Discord = require('discord.js')
|
||||||
const ytsr = require('ytsr')
|
const ytsr = require('ytsr')
|
||||||
const ytpl = require('ytpl')
|
const ytpl = require('ytpl')
|
||||||
|
const spotify = require('spotify-url-info')
|
||||||
const Queue = require('./Queue')
|
const Queue = require('./Queue')
|
||||||
const Track = require('./Track')
|
const Track = require('./Track')
|
||||||
|
|
||||||
|
@ -176,10 +176,15 @@ class Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const matchSpotifyURL = query.match(/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/)
|
||||||
|
if (matchSpotifyURL) {
|
||||||
|
const spotifyData = await spotify.getPreview(query).catch(e => resolve([]))
|
||||||
|
query = `${spotifyData.artist} - ${spotifyData.track}`
|
||||||
|
}
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
const matchURL = query.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/)
|
const matchYoutubeURL = query.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/)
|
||||||
if (matchURL) {
|
if (matchYoutubeURL) {
|
||||||
query = matchURL[1]
|
query = matchYoutubeURL[1]
|
||||||
}
|
}
|
||||||
ytsr(query, (err, results) => {
|
ytsr(query, (err, results) => {
|
||||||
if (results.items.length < 1) return resolve([])
|
if (results.items.length < 1) return resolve([])
|
||||||
|
@ -814,10 +819,11 @@ class Player {
|
||||||
if (queue.stream) queue.stream.destroy()
|
if (queue.stream) queue.stream.destroy()
|
||||||
queue.stream = newStream
|
queue.stream = newStream
|
||||||
queue.voiceConnection.play(newStream, {
|
queue.voiceConnection.play(newStream, {
|
||||||
type: 'opus'
|
type: 'opus',
|
||||||
|
bitrate: 'auto'
|
||||||
})
|
})
|
||||||
if (currentStreamTime) {
|
if (currentStreamTime) {
|
||||||
queue.voiceConnection.dispatcher.streamTime += currentStreamTime
|
queue.playing.streamTime += currentStreamTime
|
||||||
}
|
}
|
||||||
queue.voiceConnection.dispatcher.setVolumeLogarithmic(queue.calculatedVolume / 200)
|
queue.voiceConnection.dispatcher.setVolumeLogarithmic(queue.calculatedVolume / 200)
|
||||||
// When the track starts
|
// When the track starts
|
||||||
|
@ -826,6 +832,8 @@ class Player {
|
||||||
})
|
})
|
||||||
// When the track ends
|
// When the track ends
|
||||||
queue.voiceConnection.dispatcher.on('finish', () => {
|
queue.voiceConnection.dispatcher.on('finish', () => {
|
||||||
|
// reset streamTime
|
||||||
|
if (queue.repeatMode) queue.playing.streamTime = 0
|
||||||
// Play the next track
|
// Play the next track
|
||||||
return this._playTrack(queue.guildID, false)
|
return this._playTrack(queue.guildID, false)
|
||||||
})
|
})
|
||||||
|
|
258
src/Queue.js
258
src/Queue.js
|
@ -1,129 +1,129 @@
|
||||||
const Discord = require('discord.js')
|
const Discord = require('discord.js')
|
||||||
const { EventEmitter } = require('events')
|
const { EventEmitter } = require('events')
|
||||||
const Track = require('./Track')
|
const Track = require('./Track')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a guild queue.
|
* Represents a guild queue.
|
||||||
*/
|
*/
|
||||||
class Queue extends EventEmitter {
|
class Queue extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* @param {Discord.Snowflake} guildID ID of the guild this queue is for.
|
* @param {Discord.Snowflake} guildID ID of the guild this queue is for.
|
||||||
*/
|
*/
|
||||||
constructor (guildID) {
|
constructor (guildID) {
|
||||||
super()
|
super()
|
||||||
/**
|
/**
|
||||||
* ID of the guild this queue is for.
|
* ID of the guild this queue is for.
|
||||||
* @type {Discord.Snowflake}
|
* @type {Discord.Snowflake}
|
||||||
*/
|
*/
|
||||||
this.guildID = guildID
|
this.guildID = guildID
|
||||||
/**
|
/**
|
||||||
* The voice connection of this queue.
|
* The voice connection of this queue.
|
||||||
* @type {Discord.VoiceConnection}
|
* @type {Discord.VoiceConnection}
|
||||||
*/
|
*/
|
||||||
this.voiceConnection = null
|
this.voiceConnection = null
|
||||||
/**
|
/**
|
||||||
* The song currently played.
|
* The song currently played.
|
||||||
* @type {Track}
|
* @type {Track}
|
||||||
*/
|
*/
|
||||||
this.playing = null
|
this.playing = 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[]}
|
||||||
*/
|
*/
|
||||||
this.tracks = []
|
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 track was skipped.
|
* Whether the last track was skipped.
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.lastSkipped = false
|
this.lastSkipped = false
|
||||||
/**
|
/**
|
||||||
* The stream volume of this queue. (0-100)
|
* The stream volume of this queue. (0-100)
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.volume = 100
|
this.volume = 100
|
||||||
/**
|
/**
|
||||||
* Whether the stream is currently paused.
|
* Whether the stream is currently paused.
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this.paused = 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
|
* Filters status
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
this.filters = {}
|
this.filters = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
get calculatedVolume () {
|
get calculatedVolume () {
|
||||||
return this.filters.bassboost ? this.volume + 40 : this.volume
|
return this.filters.bassboost ? this.volume + 50 : this.volume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Queue
|
module.exports = Queue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when the queue is empty.
|
* Emitted when the queue is empty.
|
||||||
* @event Queue#end
|
* @event Queue#end
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* client.on('message', (message) => {
|
* client.on('message', (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();
|
||||||
*
|
*
|
||||||
* if(command === 'play'){
|
* if(command === 'play'){
|
||||||
*
|
*
|
||||||
* let track = await client.player.play(message.member.voice.channel, args[0]);
|
* let track = await client.player.play(message.member.voice.channel, args[0]);
|
||||||
*
|
*
|
||||||
* track.queue.on('end', () => {
|
* track.queue.on('end', () => {
|
||||||
* message.channel.send('The queue is empty, please add new tracks!');
|
* message.channel.send('The queue is empty, please add new tracks!');
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when the voice channel is empty.
|
* Emitted when the voice channel is empty.
|
||||||
* @event Queue#channelEmpty
|
* @event Queue#channelEmpty
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when the track changes.
|
* Emitted when the track changes.
|
||||||
* @event Queue#trackChanged
|
* @event Queue#trackChanged
|
||||||
* @param {Track} oldTrack The old track (playing before)
|
* @param {Track} oldTrack The old track (playing before)
|
||||||
* @param {Track} newTrack The new track (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
|
* @example
|
||||||
* client.on('message', (message) => {
|
* client.on('message', (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();
|
||||||
*
|
*
|
||||||
* if(command === 'play'){
|
* if(command === 'play'){
|
||||||
*
|
*
|
||||||
* let track = await client.player.play(message.member.voice.channel, args[0]);
|
* let track = await client.player.play(message.member.voice.channel, args[0]);
|
||||||
*
|
*
|
||||||
* track.queue.on('trackChanged', (oldTrack, newTrack, skipped, repeatMode) => {
|
* track.queue.on('trackChanged', (oldTrack, newTrack, skipped, repeatMode) => {
|
||||||
* if(repeatMode){
|
* if(repeatMode){
|
||||||
* message.channel.send(`Playing ${newTrack} again...`);
|
* message.channel.send(`Playing ${newTrack} again...`);
|
||||||
* } else {
|
* } else {
|
||||||
* message.channel.send(`Now playing ${newTrack}...`);
|
* message.channel.send(`Now playing ${newTrack}...`);
|
||||||
* }
|
* }
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -56,6 +56,11 @@ class Track {
|
||||||
* @type {Queue}
|
* @type {Queue}
|
||||||
*/
|
*/
|
||||||
this.queue = queue
|
this.queue = queue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream time of the track (available on applying filters)
|
||||||
|
*/
|
||||||
|
this.streamTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue