discord-player-music
Version:
A powerful music module for your Discord bot with lots of features
870 lines (869 loc) • 38.6 kB
JavaScript
"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
*/
/***********************************************************************************************************/