UNPKG

discord-player-music

Version:

A powerful music module for your Discord bot with lots of features

870 lines (869 loc) 38.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Player = void 0; const yt_search_1 = __importDefault(require("yt-search")); const lyrics_finder_1 = __importDefault(require("lyrics-finder")); const voice_1 = require("@discordjs/voice"); const errors_json_1 = __importDefault(require("./data/errors.json")); const PlaylistsManager_1 = require("./managers/PlaylistsManager"); const DatabaseManager_1 = require("./managers/DatabaseManager"); const FiltersManager_1 = require("./managers/FiltersManager"); const QueueManager_1 = require("./managers/QueueManager"); const VoiceManager_1 = require("./managers/VoiceManager"); const UtilsManager_1 = require("./managers/UtilsManager"); const PlayerError_1 = require("./PlayerError"); const PlayerEmitter_1 = require("./PlayerEmitter"); const PlayerEnums_1 = require("./PlayerEnums"); const ms_1 = __importDefault(require("./modules/ms")); const dpm_ytdl_1 = __importDefault(require("./modules/dpm-ytdl")); /** * Class that controls Player System * * @class * @classdesc Player Class * @extends {PlayerEmitter} */ class Player extends PlayerEmitter_1.PlayerEmitter { client; ready; readyTimestamp; author; website; version; filters; queue; voice; utils; database; playlists; errors; options; /** * @constructor * * @param {Client} client Discord Client * @param {PlayerOptions} [options] Module Options */ constructor(client, options) { super(); if (!client) throw new PlayerError_1.PlayerError(errors_json_1.default.default.requireParam.replace('{param}', 'client').replace('{data}', '<Player>')); /** * Discord Client * * @type {Client} * @private */ this.client = client; /** * Player Ready Status * * @type {boolean} * @private */ this.ready = false; /** * Player Ready Timestamp * * @type {number} * @private */ this.readyTimestamp = null; /** * Player Author * * @type {string} */ this.author = require('../package.json').author; /** * Player Website URL * * @type {string} */ this.website = require('../package.json').homepage; /** * Player Version * * @type {string} */ this.version = require('../package.json').version; /** * Player Utils Manager * * @type {UtilsManager} */ this.utils = new UtilsManager_1.UtilsManager(this); /** * Player Errors JSON * * @type {object} */ this.errors = errors_json_1.default; /** * Player Options * * @type {PlayerOptions} */ this.options = this.utils.checkOptions(options); /** * Player Queue Manager * * @type {QueueManager} */ this.queue = new QueueManager_1.QueueManager(this); /** * Player Voice Manager * * @type {VoiceManager} */ this.voice = new VoiceManager_1.VoiceManager(this); /** * Player Filters Manager * * @type {FiltersManager} */ this.filters = new FiltersManager_1.FiltersManager(this); /** * Player Database Manager * * @type {DatabaseManager} */ this.database = new DatabaseManager_1.DatabaseManager(this); /** * Player Playlists Manager * * @type {PlaylistsManager} */ this.playlists = new PlaylistsManager_1.PlaylistsManager(this); this.init(); this.on(PlayerEnums_1.Events.ERROR, async (data) => { if (data.channel.text) { if (data.error.message.includes('Status code: 403')) { this.queue.get(data.channel.text.guild.id).then(async (guildQueue) => { if (guildQueue?.channel) return this.initGuildTrack(guildQueue.channel.text.guild.id, guildQueue.tracks[0]); }); } } else { this.queue.get(data.channel.text.guild.id).then(async (guildQueue) => { if (guildQueue?.channel) { const guildConnection = guildQueue.connection; if (guildConnection.state.status != voice_1.VoiceConnectionStatus.Destroyed) { guildConnection.destroy(); return this.queue.delete(guildQueue.channel.text.guild.id); } else this.queue.delete(guildQueue.channel.text.guild.id); } }); } }); } /** * Player Ready Timestamp * * @type {number} * * @returns {number} Player ready timestamp */ get readyAt() { return this.readyTimestamp; } /** * Player is ready? * * @type {boolean} * * @returns {boolean} Player ready status */ get isReady() { return this.ready; } /** * Allows you to initialize the next track for playback * * @param {string} guildID Discord Guild ID * @param {PlayerTrack} track Track info * * @fires Player#error * @fires Player#playingTrack * @fires Player#queueEnded * @private * * @example * const searchResults = await client.player.search('Query', message.member, message.channel); * if(searchResults?.error) return message.channel.send({ content: `No results found!` }); * * client.player.initGuildTrack('GuildID', searchResults[0]); * * @returns {Promise<void | ErrorData>} Void or error object */ initGuildTrack(guildID, track) { return new Promise(async (res, rej) => { if (!guildID) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'guildID').replace('{data}', '<Player>.initGuildTrack')); const guildQueue = await this.queue.get(guildID); if ('error' in guildQueue) return res({ error: { code: 404, message: this.errors.default.missingQueue.replace('{id}', guildID) } }); const guildConnection = guildQueue.connection; if (!track) { if (guildQueue.tracks) { if (guildConnection.state.status != voice_1.VoiceConnectionStatus.Destroyed) { guildConnection.destroy(); this.queue.delete(guildID); return this.emit(PlayerEnums_1.Events.QUEUE_ENDED, guildQueue); } else return this.queue.delete(guildID); } } try { const stream = await (0, dpm_ytdl_1.default)(track.url, { filter: guildQueue.filter.value }); const resource = (0, voice_1.createAudioResource)(stream, { inputType: voice_1.StreamType.OggOpus, inlineVolume: true }); const guildDispatcher = guildQueue.dispatcher; try { await (0, voice_1.entersState)(guildConnection, voice_1.VoiceConnectionStatus.Ready, 5_000); guildDispatcher.play(resource); guildDispatcher.setMaxListeners(0); guildConnection.state.subscription?.player ? false : guildConnection.subscribe(guildDispatcher); } catch (error) { if (guildQueue) this.queue.delete(guildID); if (guildConnection.state.status != voice_1.VoiceConnectionStatus.Destroyed) guildConnection.destroy(); return this.emit(PlayerEnums_1.Events.ERROR, { channel: { text: track.channel.text, voice: track.channel.voice }, requested: track.requested, method: 'initGuildTrack', error: error }); } guildDispatcher.once(voice_1.AudioPlayerStatus.Idle, async () => { if (guildQueue.tracks.length <= 0) { if (guildConnection.state.status != voice_1.VoiceConnectionStatus.Destroyed) { guildConnection.destroy(); this.queue.delete(guildID); } else this.queue.delete(guildID); return this.emit(PlayerEnums_1.Events.QUEUE_ENDED, guildQueue); } if (guildQueue.loop.track) this.initGuildTrack(guildID, guildQueue.tracks[0]); if (guildQueue.loop.queue) { const lastTrack = guildQueue.tracks.shift(); guildQueue.tracks.push(lastTrack); this.initGuildTrack(guildID, guildQueue.tracks[0]); } if (!guildQueue.loop.track && !guildQueue.loop.queue) { guildQueue.tracks.shift(); this.initGuildTrack(guildID, guildQueue.tracks[0]); } }); guildDispatcher.on('error', async (error) => { if (guildQueue) this.queue.delete(guildID); this.emit(PlayerEnums_1.Events.ERROR, { channel: { text: track.channel.text, voice: track.channel.voice }, requested: track.requested, method: 'initGuildTrack', error: error }); }); guildDispatcher.state.resource.volume.setVolume(guildQueue.volume / this.options.defaultVolume); return this.emit(PlayerEnums_1.Events.TRACK_PLAYING, guildQueue.tracks[0]); } catch (error) { return this.emit(PlayerEnums_1.Events.ERROR, { channel: { text: track.channel.text, voice: track.channel.voice }, requested: track.requested, method: 'initGuildTrack', error: error }); } }); } /** * Allows you to search for tracks in YouTube by link/title * * @param {string} query Search query * @param {GuildMember} member Discord Guild Member * @param {TextChannel} channel Discord Guild Text Channel * @param {boolean} [isPlaylist] Search type * * @example * const searchData = await client.player.search('Xtrullor - Samsara', message.member, message.channel); * * console.log(searchData); * * @returns {Promise<Array<PlayerTrack> | ErrorData>} Array with search results or error object */ search(query, member, channel, isPlaylist) { return new Promise(async (res, rej) => { if (!query) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'query').replace('{data}', '<Player>.search')); if (!member) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'member').replace('{data}', '<Player>.search')); if (!channel) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'channel').replace('{data}', '<Player>.search')); const voiceChannel = member?.voice?.channel; if (!voiceChannel) return res({ error: { code: 404, message: this.errors.voice.notConnected.replace('{id}', member.user.id) } }); if (query.startsWith('http://') || query.startsWith('https://')) { const trackInfo = await dpm_ytdl_1.default.getInfo(query).catch((error) => { return res({ error: { code: 404, message: this.errors.other.unknownTrack.replace('{data}', query) } }); }); if (!trackInfo) return res({ error: { code: 404, message: this.errors.other.unknownTrack.replace('{data}', query) } }); const duration = this.utils.formatnumbers([Math.floor(Number(trackInfo.videoDetails.lengthSeconds) / 3600), Math.floor(Number(trackInfo.videoDetails.lengthSeconds) / 60 % 60), Math.floor(Number(trackInfo.videoDetails.lengthSeconds) % 60)]); const track = { searchType: PlayerEnums_1.Search.URL, title: trackInfo.videoDetails.title, url: trackInfo.videoDetails.video_url, thumbnail: trackInfo.videoDetails.thumbnails[0].url, author: { name: trackInfo.videoDetails.author.name, url: trackInfo.videoDetails.author.channel_url }, channel: { text: channel, voice: voiceChannel }, guild: member.guild, requested: member.user, duration: { hours: duration[0], minutes: duration[1], seconds: duration[2] } }; if (isPlaylist) this.initQueueTrack(1, [track]); else if (this.options.autoAddingTracks) this.initQueueTrack(1, [track]); return res([track]); } else { const results = []; const searchResults = await (0, yt_search_1.default)(query); if (searchResults.videos.length <= 0) return res({ error: { code: 404, message: this.errors.other.unknownTrack.replace('{data}', query) } }); for (let i = 0; searchResults.videos.length && searchResults.videos.length < this.options.searchResultsLimit ? i < searchResults.videos.length : i < this.options.searchResultsLimit; i++) { const video = searchResults.videos[i]; const duration = this.utils.formatnumbers([Math.floor(searchResults.videos[i].duration.seconds / 3600), Math.floor(searchResults.videos[i].duration.seconds / 60 % 60), Math.floor(searchResults.videos[i].duration.seconds % 60)]); results.push({ index: i + 1, searchType: PlayerEnums_1.Search.TITLE, title: video.title, url: video.url, thumbnail: video.thumbnail, author: { name: video.author.name, url: video.author.url }, channel: { text: channel, voice: voiceChannel }, guild: member.guild, requested: member.user, duration: { hours: duration[0], minutes: duration[1], seconds: duration[2] } }); } return res(results); } }); } /** * Allows you to find lyrics for songs by their name * * @param {String} query Track name * * @example * const lyricsData = await client.player.lyrics('Imagine Dragons - Thunder'); * if(lyricsData?.result) return message.channel.send({ content: `${lyricsData.query}\n\n${lyricsData.result}` }); * * @returns {Promise<LyricsData | ErrorData>} Returns a result or error object */ lyrics(query) { return new Promise(async (res, rej) => { if (!query) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'query').replace('{data}', '<Player>.getLyrics')); const result = await (0, lyrics_finder_1.default)(query); if (!result) return res({ error: { code: 404, message: this.errors.other.notLyrics.replace('{query}', query) } }); return res({ query: query, result: result }); }); } /** * Allows you to add tracks to the playback queue * * @param {number} index Track index * @param {Array<PlayerTrack>} results Array with search results * * @example * const searchData = await client.player.search('Xtrullor - Samsara', message.member, message.channel); * if(searchData[0]?.title) client.player.initQueueTrack(1, searchData); * * @returns {Promise<void>} Void */ initQueueTrack(index, results) { return new Promise(async (res, rej) => { if (!index) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'index').replace('{data}', '<Player>.initQueueTrack')); if (!results) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'results').replace('{data}', '<Player>.initQueueTrack')); return this.queue.add(results[index - 1]); }); } /** * Allows you to create a collector to select a track from a list * * @param {Collector} type Collector type * @param {Message} message Discord Guild Message * @param {Array<PlayerTrack>} results Search results * @param {string} [userID] Requested user ID * * @example * const searchData = await client.player.search('Xtrullor - Samsara', message.member, message.channel); * * // Reaction collector * if(searchData[0]?.title && searchData.length === 10) client.player.createCollector(Collector.REACTION, message, searchData, 'UserID'); * * // Message collector * if(searchData[0]?.title && searchData.length > 1) { * const msg = await message.channel.send({ content: searchData.map((track, index) => `${index + 1} ${track.title}`).join('\n') }); * client.player.createCollector(Collector.MESSAGE, msg, searchData, 'UserID'); * } * @returns {Promise<CollectorData | ErrorData>} object with received data or error */ createCollector(type, message, results, userID) { return new Promise(async (res, rej) => { if (!type) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'type').replace('{data}', '<Player>.createCollector')); switch (type) { case PlayerEnums_1.Collector.MESSAGE: { if (!message) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'message').replace('{data}', '<Player>.createReactionsCollector')); if (!results) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'results').replace('{data}', '<Player>.createReactionsCollector')); if (results.length <= 0) throw new PlayerError_1.PlayerError(this.errors.collectors.unknownResults.replace('{type}', 'Message')); const filter = (msg) => msg.author.id.includes(userID || message.author.id); const collector = message.channel.createMessageCollector({ filter, max: this.options.collectorsConfig.message.attempts, time: (0, ms_1.default)(this.options.collectorsConfig.message.time) }); collector.on('collect', async (collectMsg) => { const collectValue = Number(collectMsg.content); if (!isNaN(collectValue)) { if (collectValue <= 0) { collector.stop(); return res({ error: { code: 404, message: this.errors.collectors.smallValue.replace('{type}', 'Message').replace('{value}', collectValue.toString()).replace('{min}', '1') } }); } if (collectValue > results.length) { collector.stop(); return res({ error: { code: 404, message: this.errors.collectors.largeValue.replace('{type}', 'Message').replace('{value}', collectValue.toString()).replace('{max}', results.length.toString()) } }); } const track = results[collectValue - 1]; if (this.options.autoAddingTracks) { collector.stop(); this.initQueueTrack(collectValue, results); } else collector.stop(); return res({ index: collectValue, track: track, data: results }); } }); break; } case PlayerEnums_1.Collector.REACTION: { if (!message) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'message').replace('{data}', '<Player>.createReactionsCollector')); if (!results) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'results').replace('{data}', '<Player>.createReactionsCollector')); if (!userID) throw new PlayerError_1.PlayerError(this.errors.default.requireParam.replace('{param}', 'userID').replace('{data}', '<Player>.createReactionsCollector')); if (results.length <= 0) throw new PlayerError_1.PlayerError(this.errors.collectors.unknownResults.replace('{type}', 'Message')); if (results.length > 10) throw new PlayerError_1.PlayerError(this.errors.collectors.unknownCollector.replace('{type}', 'Reaction').replace('{value}', '10').replace('{collector}', 'Message')); const reacts = this.options.collectorsConfig.reaction.reactions; for (let i = 0; i < results.length; i++) { if (reacts[i]) await message.react(reacts[i]); } const filter = (reaction, user) => reaction.message.id === message.id && user.id === userID; const collector = message.createReactionCollector({ filter, time: (0, ms_1.default)(this.options.collectorsConfig.reaction.time), maxEmojis: 1, max: this.options.collectorsConfig.reaction.attempts }); collector.on('collect', async (r, u) => { const emoji = r.emoji.name; reacts.forEach(react => { if (emoji === react) { const index = react === '🔟' ? 10 : Number(react[0]); const track = results[index - 1]; if (this.options.autoAddingTracks) this.initQueueTrack(index, results); collector.stop(); return res({ index: index, track: track, data: results }); } }); }); break; } } }); } /** * Method for initializing module * * @private */ init() { const interval = setInterval(async () => { if (this.client.isReady()) { this.ready = true; this.readyTimestamp = Date.now(); this.emit(PlayerEnums_1.Events.READY, this); clearInterval(interval); } }, 100); } } exports.Player = Player; /** * Module Options * * @typedef {object} PlayerOptions * * @prop {boolean} [autoAddingTracks=true] Automatic addition of tracks to the queue (excluding playlists) * @prop {number} [searchResultsLimit=10] number of results to be returned when searching for tracks by title * @prop {boolean} [synchronLoop=true] Automatic synchronization of `loop.track` and `loop.queue` options * @prop {number} [defaultVolume=5] Default server queue playback volume * @prop {object} [databaseConfig] Database configuration object * @prop {string} [path=./dpm.playlists.json] Default path to module database file * @prop {string} [checkInterval=5s] Default interval for checking and updating the module database * @prop {object} [progressConfig] Progress bar configuration object * @prop {number} [size=11] Progress bar size * @prop {string} [line=▬] Progress bar line * @prop {string} [slider=🔘] Progress bar slider * @prop {object} [collectorsConfig] Collectors configuration object * @prop {object} [collectorsConfig.message] Message collector configuration object * @prop {string} [collectorsConfig.message.time=1m] Message collector run time * @prop {number} [collectorsConfig.message.attempts=1] Maximum number of attempts to select a track * @prop {object} [collectorsConfig.reaction] Reaction collector configuration object * @prop {string} [collectorsConfig.reaction.time=30s] Reaction collector ran time * @prop {number} [collectorsConfig.reaction.attempts=1] Maximum number of attempts to select a track * @prop {Array<string>} [collectorsConfig.reaction.reactions=[]] Array react for collector */ /** * @typedef {object} Filter * * @prop {string} [3D] 3D filter * @prop {string} [bassboost] Bassboost filter * @prop {string} [echo] Echo filter * @prop {string} [fadein] Fadein filter * @prop {string} [flanger] Flanger filter * @prop {string} [gate] Gate filter * @prop {string} [haas] Haas filter * @prop {string} [karaoke] Karaoke filter * @prop {string} [nightcore] Nightcore filter * @prop {string} [reverse] Reverse filter * @prop {string} [vaporwave] Vaporwave filter * @prop {string} [mcompand] MCompand filter * @prop {string} [phaser] Phaser filter * @prop {string} [tremolo] Tremolo filter * @prop {string} [surround] Surround filter * @prop {string} [slowed] Slowed filter * @prop {string} [earwax] Earwax filter * @prop {string} [underwater] Underwater filter * @prop {string} [clear] Clear filter */ /******************************************* Player Events JSDoc *******************************************/ /** * Emits when the module is ready * * @event Player#ready * * @param {Player} player Player instance */ /** * Emitted when a module can't do anything and gets an error * @event Player#error * * @param {object} [channel] Error Channels * @param {TextChannel} [channel.text] Discord Guild text Channel * @param {VoiceChannel} [channel.voice] Discord Guild Voice Channel * @param {User} requested Requested User * @param {string} method Module method * @param {Error} error Error object */ /** * Emitted when a new track is added to the server queue * @event Player#addedTrack * * @param {number} [index] Track index * @param {string} searchType Search type * @param {string} title Track title * @param {string} url Track URL * @param {string} thumbnail Track thumbnail * @param {object} author Track author object * @param {string} author.name Track author name * @param {string} author.url Track author channel URL * @param {object} channel Track active channels * @param {TextChannel} channel.text Discord Guild Text Channel * @param {VoiceChannel} channel.voice Discord Guild Voice Channel * @param {Guild} guild Discord Guild * @param {User} requested Requested track user object * @param {object} duration Track duration * @param {string} duration.hours Track duration in hours * @param {string} duration.minutes Track duration in minutes * @param {string} duration.seconds Track duration in seconds */ /** * Emitted when a new track starts playing in the server queue * @event Player#playingTrack * * @param {number} [index] Track index * @param {string} searchType Search type * @param {string} title Track title * @param {string} url Track URL * @param {string} thumbnail Track thumbnail * @param {object} author Track author object * @param {string} author.name Track author name * @param {string} author.url Track author channel URL * @param {object} channel Track active channels * @param {TextChannel} channel.text Discord Guild Text Channel * @param {VoiceChannel} channel.voice Discord Guild Voice Channel * @param {Guild} guild Discord Guild * @param {User} requested Requested track user object * @param {object} duration Track duration * @param {string} duration.hours Track duration in hours * @param {string} duration.minutes Track duration in minutes * @param {string} duration.seconds Track duration in seconds */ /** * Emitted when a new server playlist is created * @event Player#createdPlaylist * * @param {string} id Playlist ID * @param {string} title Playlist Title * @param {string} author Playlist Author ID * @param {number} created Playlist create timestamp * @param {number} updated Playlist update timestamp * @param {number} duration Playlist duration * @param {number} lastPlaying Playlist last playind timestamp * @param {Array<PlaylistTrack>} tracks Playlist tracks array */ /** * Emitted when the server playlist is deleted * @event Player#deletedPlaylist * * @param {string} id Playlist ID * @param {string} title Playlist Title * @param {string} author Playlist Author ID * @param {number} created Playlist create timestamp * @param {number} updated Playlist update timestamp * @param {number} duration Playlist duration * @param {number} lastPlaying Playlist last playind timestamp * @param {Array<PlaylistTrack>} tracks Playlist tracks array */ /** * Emitted when a new server queue is started * @event Player#queueStarted * * @param {object} channel Queue active channels * @param {TextChannel} channel.text Discord Guild Text Channel * @param {VoiceChannel} channel.voice Discord Guild Voice Channel * @param {AudioPlayer} dispatcher Guild Audio Dispatcher * @param {Array<PlayerTrack>} tracks Guild Tracks Array * @param {VoiceConnection} connection Discord Guild Client Voice Connection * @param {object} loop Loop object * @param {boolean} loop.track Loop current track status * @param {boolean} loop.queue Loop guild queue status * @param {number} volume Guild queue playback volume * @param {number} startStream Timestamp start playing server queue * @param {GuildQueueState} state Queue playback status * @param {PlayerFilter} filter Queue playback filter */ /** * Emitted when the server queue finishes playing * @event Player#queueEnded * * @param {object} channel Queue active channels * @param {TextChannel} channel.text Discord Guild Text Channel * @param {VoiceChannel} channel.voice Discord Guild Voice Channel * @param {AudioPlayer} dispatcher Guild Audio Dispatcher * @param {Array<PlayerTrack>} tracks Guild Tracks Array * @param {VoiceConnection} connection Discord Guild Client Voice Connection * @param {object} loop Loop object * @param {boolean} loop.track Loop current track status * @param {boolean} loop.queue Loop guild queue status * @param {number} volume Guild queue playback volume * @param {number} startStream Timestamp start playing server queue * @param {GuildQueueState} state Queue playback status * @param {PlayerFilter} filter Queue playback filter */ /** * Emitted when the playback state of the server queue changes. * @event Player#queueStateChange * * @param {object} data * @param {PlayerQueue} data.queue Guild queue object * @param {GuildQueueState} data.oldState Guild queue old state value * @param {GuildQueueState} data.newState Guild queue new state value */ /***********************************************************************************************************/ /*************************************** Player Other Typedefs JSDoc ***************************************/ /** * @typedef {object} DefaultData * * @prop {boolean} status Operation progress status */ /** * @typedef {object} PlayerTrack * * @prop {number} [index] Track index * @prop {string} searchType Search type * @prop {string} title Track title * @prop {string} url Track URL * @prop {string} thumbnail Track thumbnail * @prop {object} author Track author object * @prop {string} author.name Track author name * @prop {string} author.url Track author channel URL * @prop {object} channel Track active channels * @prop {TextChannel} channel.text Discord Guild Text Channel * @prop {VoiceChannel} channel.voice Discord Guild Voice Channel * @prop {Guild} guild Discord Guild * @prop {User} requested Requested track user object * @prop {object} duration Track duration * @prop {string} duration.hours Track duration in hours * @prop {string} duration.minutes Track duration in minutes * @prop {string} duration.seconds Track duration in seconds */ /** * @typedef {object} PlayerQueue * * @prop {object} channel Queue active channels * @prop {TextChannel} channel.text Discord Guild Text Channel * @prop {VoiceChannel} channel.voice Discord Guild Voice Channel * @prop {AudioPlayer} dispatcher Guild Audio Dispatcher * @prop {Array<PlayerTrack>} tracks Guild Tracks Array * @prop {VoiceConnection} connection Discord Guild Client Voice Connection * @prop {object} loop Loop object * @prop {boolean} loop.track Loop current track status * @prop {boolean} loop.queue Loop guild queue status * @prop {number} volume Guild queue playback volume * @prop {number} startStream Timestamp start playing server queue * @prop {GuildQueueState} state Queue playback status * @prop {PlayerFilter} filter Queue playback filter */ /** * @typedef {object} LyricsData * * @prop {string} query Search query * @prop {string} result Lyrics content */ /** * @typedef {object} PlayerFilter * * @prop {string} name Filter name * @prop {string} value Filter value */ /** * @typedef {object} PlaylistTrack * * @prop {string} url Track URL * @prop {string} title Track Title */ /** * @typedef {object} CreatePlaylistData * * @prop {string} [title] Playlist Title * @prop {string} author Playlist Author ID * @prop {PlaylistTrack} track Playlist track object */ /** * @typedef {object} AddPlaylistData * * @prop {string} [id] Playlist ID * @prop {PlaylistTrack} track Playlist track object */ /** * @typedef {object} GuildPlaylist * * @prop {string} id Playlist ID * @prop {string} title Playlist Title * @prop {string} author Playlist Author ID * @prop {number} created Playlist create timestamp * @prop {number} updated Playlist update timestamp * @prop {number} duration Playlist duration * @prop {number} lastPlaying Playlist last playind timestamp * @prop {Array<PlaylistTrack>} tracks Playlist tracks array */ /** * @typedef {object} ErrorData * * @prop {object} error Error object * @prop {number} error.code Error code * @prop {string} error.message Error message */ /** * @typedef {object} SkipData * * @prop {PlayerTrack} current Current track object * @prop {PlayerTrack} next Next track object */ /** * @typedef {object} CollectorData * * @prop {number} index Collected index * @prop {PlayerTrack} track User selected track object * @prop {Array<PlayerTrack>} data List of available tracks */ /** * @typedef {object} ProgressData * * @prop {string} bar Progress bar * @prop {string} percents Progress percents */ /** * @typedef {object} StreamData * * @prop {object} loop Loop object * @prop {boolean} loop.track Loop current track status * @prop {boolean} loop.queue Loop guild queue status * @prop {PlayerFilter} filter Stream filter * @prop {GuildQueueState} state Stream status value * @prop {number} volume Stream volume value * @prop {object} streamTime Stream duration object * @prop {string | number} streamTime.days Stream duration in days * @prop {string | number} streamTime.hours Stream duration in hours * @prop {string | number} streamTime.minutes Stream duration in minutes * @prop {string | number} streamTime.seconds Stream duration in seconds */ /** * @typedef {object} LoopData * * @prop {boolean} track Loop track status * @prop {boolean} queue Loop queue status */ /** * @typedef {object} RemoveTrackData * * @prop {PlayerTrack} deleted Remove track object * @prop {Array<PlayerTrack>} tracks Updated tracks list */ /** * @typedef {object} PlayerError * * @prop {object} [channel] Error Channels * @prop {TextChannel} [channel.text] Discord Guild text Channel * @prop {VoiceChannel} [channel.voice] Discord Guild Voice Channel * @prop {User} requested Requested User * @prop {string} method Module method * @prop {Error} error Error object */ /** * @typedef {object} StreamOptions * * @prop {number} [seek=0] Seek value * @prop {string} [filter] Filter value */ /** * @typedef {object} DatabaseManagerConfiguration * * @prop {string} path Default path to module database file * @prop {string} checkInterval Default interval for checking and updating the module database */ /***********************************************************************************************************/