Spotify support & bug fixes (#40)

Co-authored-by: Androz2091 <androz2091@gmail.com>
This commit is contained in:
Snowflake 2020-07-04 17:08:49 +05:45 committed by GitHub
parent 5b6d1c496b
commit a0cdcbc461
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 139 deletions

View file

@ -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",

View file

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

View file

@ -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}...`);
* } * }
* }); * });
* *
* } * }
* *
* }); * });
*/ */

View file

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