feat: play soundcloud tracks (#35)

This commit is contained in:
Zack Radisic 2020-06-24 07:57:24 -04:00 committed by GitHub
parent 86cedbafad
commit 334dacd880
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 1 deletions

View file

@ -33,6 +33,7 @@
"merge-options": "^2.0.0",
"node-fetch": "^2.6.0",
"simple-youtube-api": "^5.2.1",
"soundcloud-downloader": "0.0.9",
"ytsr": "^0.1.15"
},
"devDependencies": {

View file

@ -1,6 +1,7 @@
const ytdl = require('discord-ytdl-core')
const Discord = require('discord.js')
const ytsr = require('ytsr')
const scdl = require('soundcloud-downloader')
const Queue = require('./Queue')
const Track = require('./Track')
@ -43,6 +44,7 @@ const filters = {
* @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.
* @property {string} [soundcloudClientID=""] The client ID for Soundcloud, optional
*/
/**
@ -53,7 +55,8 @@ const filters = {
const defaultPlayerOptions = {
leaveOnEnd: true,
leaveOnStop: true,
leaveOnEmpty: true
leaveOnEmpty: true,
soundcloudClientID: ''
}
class Player {
@ -164,6 +167,21 @@ class Player {
const matchURL = query.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/)
if (matchURL) {
query = matchURL[1]
} else if (this.options.soundcloudClientID) {
const scRegex = /^https?:\/\/(soundcloud\.com)\/(.*)$/
if (query.match(scRegex) && query.match(scRegex)[2]) {
const info = await scdl.getInfo(query, this.options.soundcloudClientID)
const details = {
title: info.title,
link: info.uri,
duration: info.duration,
description: info.description,
thumbnail: info.artwork_url,
views: info.playback_count,
author: { name: info.user.username }
}
return resolve([new Track(details, null, null)])
}
}
ytsr(query, (err, results) => {
if (err) return []
@ -762,6 +780,51 @@ class Player {
})
}
/**
* Play a Soundcloud stream in a channel
* @ignore
* @private
* @param {Queue} queue The queue to play
* @param {*} updateFilter Whether this method is called to update some ffmpeg filters
* @returns {Promise<void>}
*/
_playSouncloudStream (queue, updateFilter) {
return new Promise(async (resolve) => {
const currentStreamTime = updateFilter ? queue.voiceConnection.dispatcher.streamTime / 1000 : undefined
const encoderArgsFilters = []
Object.keys(queue.filters).forEach((filterName) => {
if (queue.filters[filterName]) {
encoderArgsFilters.push(filters[filterName])
}
})
let encoderArgs
if (encoderArgsFilters.length < 1) {
encoderArgs = []
} else {
encoderArgs = ['-af', encoderArgsFilters.join(',')]
}
const info = await scdl.getInfo(queue.playing.url, this.options.soundcloudClientID)
const opus = scdl.filterMedia(info.media.transcodings, { format: scdl.FORMATS.OPUS })
const newStream = await scdl.downloadFromURL(opus[0].url, this.options.soundcloudClientID)
setTimeout(() => {
queue.voiceConnection.play(newStream, {
type: 'ogg/opus'
})
queue.voiceConnection.dispatcher.setVolumeLogarithmic(queue.volume / 200)
// When the track starts
queue.voiceConnection.dispatcher.on('start', () => {
resolve()
})
// When the track ends
queue.voiceConnection.dispatcher.on('finish', () => {
// Play the next track
return this._playTrack(queue.guildID, false)
})
}, 1000)
})
}
/**
* Start playing a track in a guild
* @ignore
@ -790,6 +853,16 @@ class Player {
const nowPlaying = queue.playing = queue.repeatMode ? wasPlaying : queue.tracks.shift()
// Reset lastSkipped state
queue.lastSkipped = false
if (queue.playing.url.includes('soundcloud.com')) {
this._playSouncloudStream(queue, false).then(() => {
// Emit trackChanged event
if (!firstPlay) {
queue.emit('trackChanged', nowPlaying, wasPlaying, queue.lastSkipped, queue.repeatMode)
}
})
return
}
this._playYTDLStream(queue, false).then(() => {
// Emit trackChanged event
if (!firstPlay) {