From e1e51ddf036a2accc9eb685d9e26f09ea8b01e64 Mon Sep 17 00:00:00 2001 From: Androz2091 Date: Tue, 2 Jun 2020 13:11:12 +0200 Subject: [PATCH 01/50] :recycle: Rewrite all JSDoc documentation --- src/Player.js | 398 ++++++++++++++++++++++++++------------------------ src/Queue.js | 56 ++++--- src/Song.js | 58 -------- src/Track.js | 71 +++++++++ src/Util.js | 82 ++++++----- 5 files changed, 356 insertions(+), 309 deletions(-) delete mode 100644 src/Song.js create mode 100644 src/Track.js diff --git a/src/Player.js b/src/Player.js index 8ec0679..8098a79 100644 --- a/src/Player.js +++ b/src/Player.js @@ -1,22 +1,24 @@ const ytdl = require('ytdl-core'); -const SYA = require('simple-youtube-api'); -const mergeOptions = require('merge-options'); +const SimpleYouTubeAPI = require('simple-youtube-api'); +const Discord = require('discord.js'); -const { VoiceChannel, version } = require("discord.js"); -if(version.split('.')[0] !== '12') throw new Error("Only the master branch of discord.js library is supported for now. Install it using 'npm install discordjs/discord.js'."); const Queue = require('./Queue'); +const Track = require('./Track'); const Util = require('./Util'); -const Song = require('./Song'); /** - * Player options. - * @typedef {PlayerOptions} - * - * @property {Boolean} leaveOnEnd Whether the bot should leave the current voice channel when the queue ends. - * @property {Boolean} leaveOnStop Whether the bot should leave the current voice channel when the stop() function is used. - * @property {Boolean} leaveOnEmpty Whether the bot should leave the voice channel if there is no more member in it. - */ -const PlayerOptions = { + * @typedef PlayerOptions + * @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. + */ + +/** + * Default options for the player + * @ignore + * @type {PlayerOptions} + */ +const defaultPlayerOptions = { leaveOnEnd: true, leaveOnStop: true, leaveOnEmpty: true @@ -25,158 +27,153 @@ const PlayerOptions = { class Player { /** - * @param {Client} client Your Discord Client instance. - * @param {string} youtubeToken Your Youtube Data v3 API key. - * @param {PlayerOptions} options The PlayerOptions object. + * @param {Discord.Client} client Discord.js client + * @param {string} youtubeToken Youtube Data v3 API Key + * @param {PlayerOptions} options Player options */ constructor(client, youtubeToken, options = {}){ if(!client) throw new SyntaxError('Invalid Discord client'); if(!youtubeToken) throw new SyntaxError('Invalid Token: Token must be a String'); + /** - * Your Discord Client instance. - * @type {Client} + * Discord.js client instance + * @type {Discord.Client} */ this.client = client; /** - * Your Youtube Data v3 API key. + * YouTube API Key * @type {string} */ this.youtubeToken = youtubeToken; /** - * The Simple Youtube API Client. - * @type {Youtube} + * Simple YouTube API client instance + * @type {SimpleYouTubeAPI.YouTube} */ - this.SYA = new SYA(this.youtubeToken); + this.youtube = new SimpleYouTubeAPI.YouTube(this.youtubeToken); /** - * The guilds data. + * Player queues * @type {Queue[]} */ this.queues = []; /** - * Player options. + * Player options * @type {PlayerOptions} */ - this.options = mergeOptions(PlayerOptions, options); + this.options = defaultPlayerOptions; + for(const prop in options){ + this.options[prop] = options[prop]; + } + /** + * Utilities methods for the player + * @type {Util} + */ + this.util = new Util(this.youtube) // Listener to check if the channel is empty - client.on('voiceStateUpdate', (oldState, newState) => { - if(!this.options.leaveOnEmpty) return; - // If the member leaves a voice channel - if(!oldState.channelID || newState.channelID) return; - // Search for a queue for this channel - let queue = this.queues.find((g) => g.connection.channel.id === oldState.channelID); - if(queue){ - // If the channel is not empty - if(queue.connection.channel.members.size > 1) return; - // Disconnect from the voice channel - queue.connection.channel.leave(); - // Delete the queue - this.queues = this.queues.filter((g) => g.guildID !== queue.guildID); - // Emit end event - queue.emit('channelEmpty'); - } - }); + client.on('voiceStateUpdate', (oldState, newState) => this._handleVoiceStateUpdate.call(this, oldState, newState)); } /** - * Whether a guild is currently playing songs - * @param {string} guildID The guild ID to check - * @returns {Boolean} Whether the guild is currently playing songs + * Whether a guild is currently playing something + * @param {Discord.Snowflake} guildID The guild ID to check + * @returns {boolean} Whether the guild is currently playing tracks */ isPlaying(guildID) { return this.queues.some((g) => g.guildID === guildID); } /** - * Plays a song in a voice channel. - * @param {voiceChannel} voiceChannel The voice channel in which the song will be played. - * @param {string} songName The name of the song to play. - * @param {User} requestedBy The user who requested the song. - * @returns {Promise} + * Play a track in a voice channel + * @param {Discord.VoiceChannel} voiceChannel The voice channel in which the track will be played + * @param {Track|string} track The name of the track to play + * @param {Discord.User?} user The user who requested the track + * @returns {Promise} The played track */ - play(voiceChannel, songName, requestedBy) { + play(voiceChannel, track, user) { this.queues = this.queues.filter((g) => g.guildID !== voiceChannel.id); return new Promise(async (resolve, reject) => { - if(!voiceChannel || typeof voiceChannel !== "object") return reject("voiceChannel must be type of VoiceChannel. value="+voiceChannel); - if(typeof songName !== "string") return reject("songName must be type of string. value="+songName); - // Searches the song - let video = await Util.getFirstYoutubeResult(songName, this.SYA); - if(!video) return reject('Song not found'); - // Joins the voice channel - let connection = await voiceChannel.join(); - // Creates a new guild with data + if(!voiceChannel || typeof voiceChannel !== "object"){ + return reject(`voiceChannel must be type of VoiceChannel. value=${voiceChannel}`); + } + const connection = voiceChannel.client.voice.connections.find((c) => c.channel.id === voiceChannel.id) || await voiceChannel.join(); + if(typeof track !== "object"){ + const results = await this.util.search(track, user); + track = results[0]; + } + // Create a new guild with data let queue = new Queue(voiceChannel.guild.id); - queue.connection = connection; - let song = new Song(video, queue, requestedBy); - queue.songs.push(song); + queue.voiceConnection = connection; + // Add the track to the queue + track.requestedBy = user; + queue.tracks.push(track); // Add the queue to the list this.queues.push(queue); - // Plays the song - this._playSong(queue.guildID, true); - // Resolves the song. - resolve(song); + // Play the track + this._playTrack(queue.guildID, true); + // Resolve the track + resolve(track); }); } /** - * Pauses the current song. - * @param {string} guildID - * @returns {Promise} + * Pause the current track + * @param {Discord.Snowflake} guildID The ID of the guild where the current track should be paused + * @returns {Promise} The paused track */ - pause(guildID){ + pause (guildID) { return new Promise(async(resolve, reject) => { // Gets guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); // Pauses the dispatcher - queue.dispatcher.pause(); + queue.voiceConnection.dispatcher.pause(); queue.playing = false; // Resolves the guild queue - resolve(queue.songs[0]); + resolve(queue.tracks[0]); }); } /** - * Resumes the current song. - * @param {string} guildID - * @returns {Promise} + * Resume the current track + * @param {Discord.Snowflake} guildID The ID of the guild where the current track should be resumed + * @returns {Promise} The resumed track */ - resume(guildID){ + resume (guildID) { return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Pauses the dispatcher - queue.dispatcher.resume(); + // Pause the dispatcher + queue.voiceConnection.dispatcher.resume(); queue.playing = true; - // Resolves the guild queue - resolve(queue.songs[0]); + // Resolve the guild queue + resolve(queue.tracks[0]); }); } /** * Stops playing music. - * @param {string} guildID + * @param {Discord.Snowflake} guildID The ID of the guild where the music should be stopped * @returns {Promise} */ stop(guildID){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Stops the dispatcher + // Stop the dispatcher queue.stopped = true; - queue.songs = []; - queue.dispatcher.end(); - // Resolves + queue.tracks = []; + queue.voiceConnection.dispatcher.end(); + // Resolve resolve(); }); } /** - * Updates the volume. - * @param {string} guildID - * @param {number} percent + * Update the volume + * @param {Discord.Snowflake} guildID The ID of the guild where the music should be modified + * @param {number} percent The new volume (0-100) * @returns {Promise} */ setVolume(guildID, percent) { @@ -185,15 +182,16 @@ class Player { let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); // Updates volume - queue.dispatcher.setVolumeLogarithmic(percent / 200); + queue.voiceConnection.dispatcher.setVolumeLogarithmic(percent / 200); + queue.volume = percent; // Resolves guild queue - resolve(queue); + resolve(); }); } /** - * Gets the guild queue. - * @param {string} guildID + * Get a guild queue + * @param {Discord.Snowflake} guildID * @returns {?Queue} */ getQueue(guildID) { @@ -203,108 +201,107 @@ class Player { } /** - * Adds a song to the guild queue. - * @param {string} guildID - * @param {string} songName The name of the song to add to the queue. - * @param {User} requestedBy The user who requested the song. - * @returns {Promise} + * Add a track to the guild queue + * @param {Discord.Snowflake} guildID The ID of the guild where the track should be added + * @param {string} trackName The name of the track to add to the queue + * @param {Discord.User?} requestedBy The user who requested the track + * @returns {Promise} The added track */ - addToQueue(guildID, songName, requestedBy){ + addToQueue(guildID, trackName, requestedBy){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Searches the song - let video = await Util.getFirstYoutubeResult(songName, this.SYA).catch(() => {}); - if(!video) return reject('Song not found'); - let song = new Song(video, queue, requestedBy); - // Updates queue - queue.songs.push(song); - // Resolves the song - resolve(song); + // Search the track + let track = await this.util.search(trackName, requestedBy).catch(() => {}); + if(!track[0]) return reject('Track not found'); + // Update queue + queue.tracks.push(track[0]); + // Resolve the track + resolve(track[0]); }); } /** - * Sets the queue for a guild. - * @param {string} guildID - * @param {Array} songs The songs list - * @returns {Promise} + * Set the queue for a guild. + * @param {Discord.Snowflake} guildID The ID of the guild where the queue should be set + * @param {Track[]} tracks The tracks list + * @returns {Promise} The new queue */ - setQueue(guildID, songs){ + setQueue(guildID, tracks){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Updates queue - queue.songs = songs; - // Resolves the queue + // Update queue + queue.tracks = tracks; + // Resolve the queue resolve(queue); }); } /** - * Clears the guild queue, but not the current song. - * @param {string} guildID - * @returns {Promise} + * Clear the guild queue, but not the current track + * @param {Discord.Snowflake} guildID The ID of the guild where the queue should be cleared + * @returns {Promise} The updated queue */ clearQueue(guildID){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Clears queue - let currentlyPlaying = queue.songs.shift(); - queue.songs = [ currentlyPlaying ]; - // Resolves guild queue + // Clear queue + let currentlyPlaying = queue.tracks.shift(); + queue.tracks = [ currentlyPlaying ]; + // Resolve guild queue resolve(queue); }); } /** - * Skips a song. - * @param {string} guildID - * @returns {Promise} + * Skip a track + * @param {Discord.Snowflake} guildID The ID of the guild where the track should be skipped + * @returns {Promise} */ skip(guildID){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - let currentSong = queue.songs[0]; - // Ends the dispatcher - queue.dispatcher.end(); - queue.skipped = true; - // Resolves the current song - resolve(currentSong); + let currentTrack = queue.tracks[0]; + // End the dispatcher + queue.voiceConnection.dispatcher.end(); + queue.lastSkipped = true; + // Resolve the current track + resolve(currentTrack); }); } /** - * Gets the currently playing song. - * @param {string} guildID - * @returns {Promise} + * Get the currently playing track + * @param {Discord.Snowflake} guildID + * @returns {Promise} The track which is currently played */ nowPlaying(guildID){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - let currentSong = queue.songs[0]; - // Resolves the current song - resolve(currentSong); + let currentTrack = queue.tracks[0]; + // Resolve the current track + resolve(currentTrack); }); } /** * Enable or disable the repeat mode - * @param {string} guildID + * @param {Discord.Snowflake} guildID * @param {Boolean} enabled Whether the repeat mode should be enabled * @returns {Promise} */ setRepeatMode(guildID, enabled) { return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); // Enable/Disable repeat mode @@ -315,89 +312,116 @@ class Player { } /** - * Shuffles the guild queue. - * @param {string} guildID - * @returns {Promise} + * Shuffle the guild queue (except the first track) + * @param {Discord.Snowflake} guildID The ID of the guild where the queue should be shuffled + * @returns {Promise} The updated queue */ shuffle(guildID){ return new Promise(async(resolve, reject) => { - // Gets guild queue + // Get guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Shuffle the queue (except the first song) - let currentSong = queue.songs.shift(); - queue.songs = queue.songs.sort(() => Math.random() - 0.5); - queue.songs.unshift(currentSong); + // Shuffle the queue (except the first track) + let currentTrack = queue.tracks.shift(); + queue.tracks = queue.tracks.sort(() => Math.random() - 0.5); + queue.tracks.unshift(currentTrack); // Resolve - resolve(); + resolve(queue); }); } /** - * Removes a song from the queue - * @param {string} guildID - * @param {number|Song} song The index of the song to remove or the song to remove object. - * @returns {Promise} + * Remove a track from the queue + * @param {Discord.Snowflake} guildID The ID of the guild where the track should be removed + * @param {number|Track} track The index of the track to remove or the track to remove object + * @returns {Promise} */ - remove(guildID, song){ + remove(guildID, track){ return new Promise(async(resolve, reject) => { // Gets guild queue let queue = this.queues.find((g) => g.guildID === guildID); if(!queue) return reject('Not playing'); - // Remove the song from the queue - let songFound = null; - if(typeof song === "number"){ - songFound = queue.songs[song]; - if(songFound){ - queue.songs = queue.songs.filter((s) => s !== songFound); + // Remove the track from the queue + let trackFound = null; + if(typeof track === "number"){ + trackFound = queue.tracks[track]; + if(trackFound){ + queue.tracks = queue.tracks.filter((s) => s !== trackFound); } } else { - songFound = queue.songs.find((s) => s === song); - if(songFound){ - queue.songs = queue.songs.filter((s) => s !== songFound); + trackFound = queue.tracks.find((s) => s === track); + if(trackFound){ + queue.tracks = queue.tracks.filter((s) => s !== trackFound); } } // Resolve - resolve(songFound); + resolve(trackFound); }); } /** - * Start playing songs in a guild. + * Handle the voice state update event * @ignore - * @param {string} guildID + * @private + * @param {Discord.VoiceState} oldState + * @param {Discord.VoiceState} newState + */ + _handleVoiceStateUpdate(oldState, newState) { + if(!this.options.leaveOnEmpty) return; + // If the member leaves a voice channel + if(!oldState.channelID || newState.channelID) return; + // Search for a queue for this channel + let queue = this.queues.find((g) => g.voiceConnection.channel.id === oldState.channelID); + if(queue){ + // If the channel is not empty + if(queue.voiceConnection.channel.members.size > 1) return; + // Disconnect from the voice channel + queue.voiceConnection.channel.leave(); + // Delete the queue + this.queues = this.queues.filter((g) => g.guildID !== queue.guildID); + // Emit end event + queue.emit('channelEmpty'); + } + } + + /** + * Start playing a track in a guild + * @ignore + * @private + * @param {Discord.Snowflake} guildID * @param {Boolean} firstPlay Whether the function was called from the play() one */ - async _playSong(guildID, firstPlay) { + async _playTrack(guildID, firstPlay) { // Gets guild queue let queue = this.queues.find((g) => g.guildID === guildID); // If there isn't any music in the queue - if(queue.songs.length < 2 && !firstPlay && !queue.repeatMode){ + if(queue.tracks.length < 2 && !firstPlay && !queue.repeatMode){ // Leaves the voice channel - if(this.options.leaveOnEnd && !queue.stopped) queue.connection.channel.leave(); + if(this.options.leaveOnEnd && !queue.stopped) queue.voiceConnection.channel.leave(); // Remoces the guild from the guilds list this.queues = this.queues.filter((g) => g.guildID !== guildID); // Emits stop event if(queue.stopped){ - if(this.options.leaveOnStop) queue.connection.channel.leave(); + if(this.options.leaveOnStop) queue.voiceConnection.channel.leave(); return queue.emit('stop'); } // Emits end event return queue.emit('end'); } - // Emit songChanged event - if(!firstPlay) queue.emit('songChanged', (!queue.repeatMode ? queue.songs.shift() : queue.songs[0]), queue.songs[0], queue.skipped, queue.repeatMode); - queue.skipped = false; - let song = queue.songs[0]; - // Download the song - let dispatcher = queue.connection.play(ytdl(song.url, { filter: "audioonly" })); - queue.dispatcher = dispatcher; + // Emit trackChanged event + if(!firstPlay) queue.emit('trackChanged', (!queue.repeatMode ? queue.tracks.shift() : queue.tracks[0]), queue.tracks[0], queue.lastSkipped, queue.repeatMode); + queue.lastSkipped = false; + let track = queue.tracks[0]; + // Download the track + queue.voiceConnection.play(ytdl(track.url, { + filter: "audioonly" + })); // Set volume - dispatcher.setVolumeLogarithmic(queue.volume / 200); - // When the song ends - dispatcher.on('finish', () => { - // Play the next song - return this._playSong(guildID, false); + queue.voiceConnection.dispatcher.setVolumeLogarithmic(queue.volume / 200); + // When the track ends + queue.voiceConnection.dispatcher.on('finish', () => { + // Play the next track + return this._playTrack(guildID, false); }); } diff --git a/src/Queue.js b/src/Queue.js index dea9924..e42c727 100644 --- a/src/Queue.js +++ b/src/Queue.js @@ -1,4 +1,6 @@ -const { EventEmitter } = require('events'); +const Discord = require('discord.js') +const { EventEmitter } = require('events') +const Track = require('./Track') /** * Represents a guild queue. @@ -6,60 +8,56 @@ const { EventEmitter } = require('events'); class Queue extends EventEmitter { /** - * Represents a guild queue. - * @param {string} guildID + * @param {Discord.Snowflake} guildID ID of the guild this queue is for. */ constructor(guildID){ super(); /** - * The guild ID. - * @type {Snowflake} + * ID of the guild this queue is for. + * @type {Discord.Snowflake} */ this.guildID = guildID; /** - * The stream dispatcher. - * @type {StreamDispatcher} + * The voice connection of this queue. + * @type {Discord.VoiceConnection} */ - this.dispatcher = null; + this.voiceConnection = null; /** - * The voice connection. - * @type {VoiceConnection} + * The tracks of this queue. The first one is currenlty playing and the others are going to be played. + * @type {Track[]} */ - this.connection = null; - /** - * Songs. The first one is currently playing and the rest is going to be played. - * @type {Song[]} - */ - this.songs = []; + this.tracks = []; /** * Whether the stream is currently stopped. - * @type {Boolean} + * @type {boolean} */ this.stopped = false; /** - * Whether the last song was skipped. - * @type {Boolean} + * Whether the last track was skipped. + * @type {boolean} */ - this.skipped = false; + this.lastSkipped = false; /** - * The stream volume. - * @type {Number} + * The stream volume of this queue. (0-100) + * @type {number} */ this.volume = 100; /** * Whether the stream is currently playing. - * @type {Boolean} + * @type {boolean} */ this.playing = true; /** * Whether the repeat mode is enabled. - * @type {Boolean} + * @type {boolean} */ this.repeatMode = false; } }; +module.exports = Queue; + /** * Emitted when the queue is empty. * @event Queue#end @@ -71,11 +69,9 @@ class Queue extends EventEmitter { */ /** - * Emitted when the song changes. - * @event Queue#songChanged - * @param {Song} oldSong The old song (playing before) - * @param {Song} newSong The new song (currently playing) + * Emitted when the track changes. + * @event Queue#trackChanged + * @param {Track} oldTrack The old track (playing before) + * @param {Track} newTrack The new track (currently playing) * @param {Boolean} skipped Whether the change is due to the skip() function */ - -module.exports = Queue; \ No newline at end of file diff --git a/src/Song.js b/src/Song.js deleted file mode 100644 index d2794ed..0000000 --- a/src/Song.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Represents a song. - */ -class Song { - /** - * @param {Video} video The Youtube video - * @param {Queue} queue The queue in which the song is - */ - constructor(video, queue, requestedBy) { - /** - * Song name. - * @type {string} - */ - this.name = video.title; - /** - * Song duration. - * @type {Number} - */ - this.duration = ((video.duration.hours*3600)+(video.duration.minutes*60)+(video.duration.seconds)) * 100; - /** - * Raw video object from Simple Youtube API - * @type {Video} - */ - this.rawVideo = video; - /** - * Raw informations about the song. - * @type {Object} - */ - this.raw = video.raw; - /** - * Author channel of the song. - * @type {string} - */ - this.author = video.raw.snippet.channelTitle; - /** - * Youtube video URL. - * @type {string} - */ - this.url = `https://www.youtube.com/watch?v=${video.id}`; - /** - * Youtube video thumbnail. - * @type {string} - */ - this.thumbnail = video.raw.snippet.thumbnails.default.url; - /** - * The queue in which the song is. - * @type {Queue} - */ - this.queue = queue; - /** - * The user who requested that song. - * @type {User} - */ - this.requestedBy = requestedBy; - } -}; - -module.exports = Song; \ No newline at end of file diff --git a/src/Track.js b/src/Track.js new file mode 100644 index 0000000..b3f0133 --- /dev/null +++ b/src/Track.js @@ -0,0 +1,71 @@ +const Discord = require('discord.js') +const Queue = require('./Queue') +const SimpleYouTubeAPI = require('simple-youtube-api') + +/** + * Represents a track. + */ +class Track { + + /** + * @param {SimpleYouTubeAPI.Video} video The video for this track + * @param {Discord.User?} user The user who requested the track + * @param {Queue?} queue The queue in which is the track is + */ + constructor(video, user, queue) { + /** + * The track name + * @type {string} + */ + this.name = video.title; + /** + * The full video object + * @type {SimpleYouTubeAPI.Video} + */ + this.data = video; + /** + * The Youtube URL of the track + * @type {string} + */ + this.url = `https://www.youtube.com/watch?v=${video.id}`; + /** + * The user who requested the track + * @type {Discord.User?} + */ + this.requestedBy = user; + /** + * The queue in which the track is + * @type {Queue} + */ + this.queue = queue; + } + + /** + * The name of the channel which is the author of the video on Youtube + * @type {string} + */ + get author() { + return this.data.raw.snippet.channelTitle; + } + + /** + * The Youtube video thumbnail + * @type {string} + */ + get thumbnail() { + return this.data.raw.snippet.thumbnails.default.url; + } + + /** + * The track duration + * @type {number} + */ + get duration() { + return typeof this.data.duration === "object" + ? ((this.data.duration.hours*3600)+(this.data.duration.minutes*60)+(this.data.duration.seconds)) * 1000 + : parseInt(this.data.duration) + } + +}; + +module.exports = Track; diff --git a/src/Util.js b/src/Util.js index 683c198..0a92906 100644 --- a/src/Util.js +++ b/src/Util.js @@ -1,49 +1,63 @@ -const fetch = require('node-fetch'); +const fetch = require('node-fetch') +const Discord = require('discord.js') +const Track = require('./Track') +const SimpleYouTubeAPI = require('simple-youtube-api') /** - * Utilities. - * @ignore + * Utilities */ class Util { - constructor(){} - /** - * Gets the first youtube results for your search. - * @param {string} search The name of the video or the video URL. - * @param {Youtube} SYA The Simple Youtube API Client. - * @returns {Promise