From 15e223a50e48a6005795025bbc0f77095c3b6472 Mon Sep 17 00:00:00 2001 From: Thaddeus Kuah Date: Sun, 10 Jan 2021 23:38:28 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Support=20for=20Spotify=20playlists?= =?UTF-8?q?=20and=20albums=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Player.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/Util.js | 10 ++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/Player.js b/src/Player.js index 396f11f..42b5ae2 100644 --- a/src/Player.js +++ b/src/Player.js @@ -141,6 +141,8 @@ class Player extends EventEmitter { return 'youtube-video' } else if (this.util.isSoundcloudLink(query)) { return 'soundcloud-song' + } else if (this.util.isSpotifyPLLink(query)) { + return 'spotify-playlist' } else { return 'youtube-video-keywords' } @@ -331,7 +333,63 @@ class Player extends EventEmitter { this._addTracksToQueue(message, playlist.tracks) } } - + async _handleSpotifyPlaylist (message, query) { + const playlist = await spotify.getData(query) + if (!playlist) return this.emit('noResults', message, query) + let tracks = [] + let s; + for (var i = 0; i < playlist.tracks.items.length; i++) { + let query = `${playlist.tracks.items[i].track.artists[0].name} - ${playlist.tracks.items[i].track.name}` + let results = await ytsr.search(query, { type: 'video' }) + if (results.length < 1) { + s++ // could be used later for skipped tracks due to result not being found + continue; + } + tracks.push(results[0]) + } + playlist.tracks = tracks.map((item) => new Track(item, message.author)) + playlist.duration = playlist.tracks.reduce((prev, next) => prev + next.duration, 0) + playlist.thumbnail = playlist.images[0].url + playlist.requestedBy = message.author + if (this.isPlaying(message)) { + const queue = this._addTracksToQueue(message, playlist.tracks) + this.emit('playlistAdd', message, queue, playlist) + } else { + const track = playlist.tracks.shift() + const queue = await this._createQueue(message, track).catch((e) => this.emit('error', e, message)) + this.emit('trackStart', message, queue.tracks[0]) + this._addTracksToQueue(message, playlist.tracks) + } + } + async _handleSpotifyAlbum (message, query) { + const album = await spotify.getData(query) + if (!album) return this.emit('noResults', message, query) + let tracks = [] + let s; + for (var i = 0; i < album.tracks.items.length; i++) { + let query = `${album.tracks.items[i].artists[0].name} - ${album.tracks.items[i].name}` + let results = await ytsr.search(query, { type: 'video' }) + if (results.length < 1) { + s++ // could be used later for skipped tracks due to result not being found + continue; + } + tracks.push(results[0]) + } + + album.tracks = tracks.map((item) => new Track(item, message.author)) + album.duration = album.tracks.reduce((prev, next) => prev + next.duration, 0) + album.thumbnail = album.images[0].url + album.requestedBy = message.author + if (this.isPlaying(message)) { + const queue = this._addTracksToQueue(message, album.tracks) + this.emit('playlistAdd', message, queue, album) + } else { + const track = album.tracks.shift() + const queue = await this._createQueue(message, track).catch((e) => this.emit('error', e, message)) + this.emit('trackStart', message, queue.tracks[0]) + this._addTracksToQueue(message, album.tracks) + } + } /** * Play a track in the server. Supported query types are `keywords`, `YouTube video links`, `YouTube playlists links`, `Spotify track link` or `SoundCloud song link`. * @param {Discord.Message} message Discord `message` @@ -346,6 +404,12 @@ class Player extends EventEmitter { if (this.util.isYTPlaylistLink(query)) { return this._handlePlaylist(message, query) } + if (this.util.isSpotifyPLLink(query)) { + return this._handleSpotifyPlaylist(message, query) + } + if (this.util.isSpotifyAlbumLink(query)) { + return this._handleSpotifyAlbum(message, query) + } let trackToPlay if (query instanceof Track) { trackToPlay = query diff --git a/src/Util.js b/src/Util.js index 9f6a370..572e39a 100644 --- a/src/Util.js +++ b/src/Util.js @@ -2,6 +2,8 @@ const ytsr = require('youtube-sr') const soundcloud = require('soundcloud-scraper') const spotifySongRegex = (/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/) +const spotifyPlaylistRegex = (/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:playlist\/|\?uri=spotify:playlist:)((\w|-){22})/) +const spotifyAlbumRegex = (/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:album\/|\?uri=spotify:album:)((\w|-){22})/) module.exports = class Util { constructor () { @@ -20,6 +22,14 @@ module.exports = class Util { return spotifySongRegex.test(query) } + static isSpotifyPLLink (query) { + return spotifyPlaylistRegex.test(query) + } + + static isSpotifyAlbumLink(query) { + return spotifyAlbumRegex.test(query) + } + static isYTPlaylistLink (query) { return ytsr.validate(query, 'PLAYLIST') }