re-implement voice
This commit is contained in:
parent
8d80d82caa
commit
4acd25a6ab
3 changed files with 147 additions and 64 deletions
|
@ -22,6 +22,8 @@ class Queue<T = unknown> {
|
||||||
private _streamTime: number = 0;
|
private _streamTime: number = 0;
|
||||||
public _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
|
public _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
|
||||||
private _activeFilters: any[] = [];
|
private _activeFilters: any[] = [];
|
||||||
|
private _filtersUpdate = false;
|
||||||
|
public destroyed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue constructor
|
* Queue constructor
|
||||||
|
@ -50,6 +52,42 @@ class Queue<T = unknown> {
|
||||||
*/
|
*/
|
||||||
this.options = {};
|
this.options = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this queue is destroyed
|
||||||
|
* @type {boolean}
|
||||||
|
* @name Queue#destroyed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue repeat mode
|
||||||
|
* @type {QueueRepeatMode}
|
||||||
|
* @name Queue#repeatMode
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue metadata
|
||||||
|
* @type {any}
|
||||||
|
* @name Queue#metadata
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous tracks
|
||||||
|
* @type {Track[]}
|
||||||
|
* @name Queue#previousTracks
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular tracks
|
||||||
|
* @type {Track[]}
|
||||||
|
* @name Queue#tracks
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connection
|
||||||
|
* @type {StreamDispatcher}
|
||||||
|
* @name Queue#connection
|
||||||
|
*/
|
||||||
|
|
||||||
Object.assign(
|
Object.assign(
|
||||||
this.options,
|
this.options,
|
||||||
{
|
{
|
||||||
|
@ -70,6 +108,7 @@ class Queue<T = unknown> {
|
||||||
* @type {Track}
|
* @type {Track}
|
||||||
*/
|
*/
|
||||||
get current() {
|
get current() {
|
||||||
|
this.#watchDestroyed();
|
||||||
return this.connection.audioResource?.metadata ?? this.tracks[0];
|
return this.connection.audioResource?.metadata ?? this.tracks[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +117,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {Track}
|
* @returns {Track}
|
||||||
*/
|
*/
|
||||||
nowPlaying() {
|
nowPlaying() {
|
||||||
|
this.#watchDestroyed();
|
||||||
return this.current;
|
return this.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,22 +127,47 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<Queue>}
|
* @returns {Promise<Queue>}
|
||||||
*/
|
*/
|
||||||
async connect(channel: StageChannel | VoiceChannel) {
|
async connect(channel: StageChannel | VoiceChannel) {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!["stage", "voice"].includes(channel?.type)) throw new TypeError(`Channel type must be voice or stage, got ${channel?.type}!`);
|
if (!["stage", "voice"].includes(channel?.type)) throw new TypeError(`Channel type must be voice or stage, got ${channel?.type}!`);
|
||||||
const connection = await this.player.voiceUtils.connect(channel, {
|
const connection = await this.player.voiceUtils.connect(channel, {
|
||||||
deaf: this.options.autoSelfDeaf
|
deaf: this.options.autoSelfDeaf
|
||||||
});
|
});
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
|
||||||
// it's ok to use this here since Queue listens to the events 1 time per play and destroys the listener
|
|
||||||
this.connection.setMaxListeners(Infinity);
|
|
||||||
|
|
||||||
if (channel.type === "stage") await channel.guild.me.voice.setRequestToSpeak(true).catch(() => {});
|
if (channel.type === "stage") await channel.guild.me.voice.setRequestToSpeak(true).catch(() => {});
|
||||||
|
|
||||||
this.connection.on("error", (err) => this.player.emit("error", this, err));
|
this.connection.on("error", (err) => this.player.emit("connectionError", this, err));
|
||||||
this.connection.on("debug", (msg) => this.player.emit("debug", this, msg));
|
this.connection.on("debug", (msg) => this.player.emit("debug", this, msg));
|
||||||
|
|
||||||
this.player.emit("connectionCreate", this, this.connection);
|
this.player.emit("connectionCreate", this, this.connection);
|
||||||
|
|
||||||
|
this.connection.on("start", () => {
|
||||||
|
this.playing = true;
|
||||||
|
if (!this._filtersUpdate) this.player.emit("trackStart", this, this.current);
|
||||||
|
this._filtersUpdate = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.connection.on("finish", async () => {
|
||||||
|
this.playing = false;
|
||||||
|
if (this._filtersUpdate) return;
|
||||||
|
this._streamTime = 0;
|
||||||
|
|
||||||
|
if (!this.tracks.length && this.repeatMode === QueueRepeatMode.OFF) {
|
||||||
|
if (this.options.leaveOnEnd) this.destroy();
|
||||||
|
this.player.emit("queueEnd", this);
|
||||||
|
} else {
|
||||||
|
if (this.repeatMode !== QueueRepeatMode.AUTOPLAY) {
|
||||||
|
if (this.repeatMode === QueueRepeatMode.TRACK) return void this.play(Util.last(this.previousTracks), { immediate: true });
|
||||||
|
if (this.repeatMode === QueueRepeatMode.QUEUE) this.tracks.push(Util.last(this.previousTracks));
|
||||||
|
const nextTrack = this.tracks.shift();
|
||||||
|
this.play(nextTrack, { immediate: true });
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this._handleAutoplay(Util.last(this.previousTracks));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +180,8 @@ class Queue<T = unknown> {
|
||||||
this.connection.end();
|
this.connection.end();
|
||||||
if (disconnect) this.connection.disconnect();
|
if (disconnect) this.connection.disconnect();
|
||||||
this.player.queues.delete(this.guild.id);
|
this.player.queues.delete(this.guild.id);
|
||||||
|
this.player.voiceUtils.cache.delete(this.guild.id);
|
||||||
|
this.destroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,7 +189,9 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
skip() {
|
skip() {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection) return false;
|
if (!this.connection) return false;
|
||||||
|
this._filtersUpdate = false;
|
||||||
this.connection.end();
|
this.connection.end();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -133,6 +202,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
addTrack(track: Track) {
|
addTrack(track: Track) {
|
||||||
|
this.#watchDestroyed();
|
||||||
this.tracks.push(track);
|
this.tracks.push(track);
|
||||||
this.player.emit("trackAdd", this, track);
|
this.player.emit("trackAdd", this, track);
|
||||||
}
|
}
|
||||||
|
@ -142,6 +212,7 @@ class Queue<T = unknown> {
|
||||||
* @param {Track[]} tracks Array of tracks to add
|
* @param {Track[]} tracks Array of tracks to add
|
||||||
*/
|
*/
|
||||||
addTracks(tracks: Track[]) {
|
addTracks(tracks: Track[]) {
|
||||||
|
this.#watchDestroyed();
|
||||||
this.tracks.push(...tracks);
|
this.tracks.push(...tracks);
|
||||||
this.player.emit("tracksAdd", this, tracks);
|
this.player.emit("tracksAdd", this, tracks);
|
||||||
}
|
}
|
||||||
|
@ -152,6 +223,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
setPaused(paused?: boolean) {
|
setPaused(paused?: boolean) {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection) return false;
|
if (!this.connection) return false;
|
||||||
return paused ? this.connection.pause(true) : this.connection.resume();
|
return paused ? this.connection.pause(true) : this.connection.resume();
|
||||||
}
|
}
|
||||||
|
@ -162,6 +234,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
setBitrate(bitrate: number | "auto") {
|
setBitrate(bitrate: number | "auto") {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection?.audioResource?.encoder) return;
|
if (!this.connection?.audioResource?.encoder) return;
|
||||||
if (bitrate === "auto") bitrate = this.connection.channel?.bitrate ?? 64000;
|
if (bitrate === "auto") bitrate = this.connection.channel?.bitrate ?? 64000;
|
||||||
this.connection.audioResource.encoder.setBitrate(bitrate);
|
this.connection.audioResource.encoder.setBitrate(bitrate);
|
||||||
|
@ -173,6 +246,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
setVolume(amount: number) {
|
setVolume(amount: number) {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection) return false;
|
if (!this.connection) return false;
|
||||||
this.options.initialVolume = amount;
|
this.options.initialVolume = amount;
|
||||||
return this.connection.setVolume(amount);
|
return this.connection.setVolume(amount);
|
||||||
|
@ -183,6 +257,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
setRepeatMode(mode: QueueRepeatMode) {
|
setRepeatMode(mode: QueueRepeatMode) {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (![QueueRepeatMode.OFF, QueueRepeatMode.QUEUE, QueueRepeatMode.TRACK, QueueRepeatMode.AUTOPLAY].includes(mode)) throw new Error(`Unknown repeat mode "${mode}"!`);
|
if (![QueueRepeatMode.OFF, QueueRepeatMode.QUEUE, QueueRepeatMode.TRACK, QueueRepeatMode.AUTOPLAY].includes(mode)) throw new Error(`Unknown repeat mode "${mode}"!`);
|
||||||
if (mode === this.repeatMode) return false;
|
if (mode === this.repeatMode) return false;
|
||||||
this.repeatMode = mode;
|
this.repeatMode = mode;
|
||||||
|
@ -194,6 +269,7 @@ class Queue<T = unknown> {
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
get volume() {
|
get volume() {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection) return 100;
|
if (!this.connection) return 100;
|
||||||
return this.connection.volume;
|
return this.connection.volume;
|
||||||
}
|
}
|
||||||
|
@ -207,6 +283,7 @@ class Queue<T = unknown> {
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
get streamTime() {
|
get streamTime() {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection) return 0;
|
if (!this.connection) return 0;
|
||||||
const playbackTime = this._streamTime + this.connection.streamTime;
|
const playbackTime = this._streamTime + this.connection.streamTime;
|
||||||
const NC = this._activeFilters.includes("nightcore") ? 1.25 : null;
|
const NC = this._activeFilters.includes("nightcore") ? 1.25 : null;
|
||||||
|
@ -221,6 +298,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {AudioFilters}
|
* @returns {AudioFilters}
|
||||||
*/
|
*/
|
||||||
getFiltersEnabled() {
|
getFiltersEnabled() {
|
||||||
|
this.#watchDestroyed();
|
||||||
return AudioFilters.names.filter((x) => this._activeFilters.includes(x));
|
return AudioFilters.names.filter((x) => this._activeFilters.includes(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +307,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {AudioFilters}
|
* @returns {AudioFilters}
|
||||||
*/
|
*/
|
||||||
getFiltersDisabled() {
|
getFiltersDisabled() {
|
||||||
|
this.#watchDestroyed();
|
||||||
return AudioFilters.names.filter((x) => !this._activeFilters.includes(x));
|
return AudioFilters.names.filter((x) => !this._activeFilters.includes(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,6 +317,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async setFilters(filters?: QueueFilters) {
|
async setFilters(filters?: QueueFilters) {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!filters || !Object.keys(filters).length) {
|
if (!filters || !Object.keys(filters).length) {
|
||||||
// reset filters
|
// reset filters
|
||||||
const streamTime = this.streamTime;
|
const streamTime = this.streamTime;
|
||||||
|
@ -276,6 +356,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
async seek(position: number) {
|
async seek(position: number) {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.playing || !this.current) return false;
|
if (!this.playing || !this.current) return false;
|
||||||
if (position < 1) position = 0;
|
if (position < 1) position = 0;
|
||||||
if (position >= this.current.durationMS) return this.skip();
|
if (position >= this.current.durationMS) return this.skip();
|
||||||
|
@ -294,7 +375,17 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async back() {
|
async back() {
|
||||||
return await this.play(Util.last(this.previousTracks), { immediate: true });
|
this.#watchDestroyed();
|
||||||
|
const prev = this.previousTracks[this.previousTracks.length - 2]; // because last item is the current track
|
||||||
|
if (!prev) throw new Error("Could not find previous track");
|
||||||
|
|
||||||
|
return await this.play(prev, { immediate: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#watchDestroyed();
|
||||||
|
this.tracks = [];
|
||||||
|
this.previousTracks = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -304,6 +395,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async play(src?: Track, options: PlayOptions = {}): Promise<void> {
|
async play(src?: Track, options: PlayOptions = {}): Promise<void> {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.connection || !this.connection.voiceConnection) throw new Error("Voice connection is not available, use <Queue>.connect()!");
|
if (!this.connection || !this.connection.voiceConnection) throw new Error("Voice connection is not available, use <Queue>.connect()!");
|
||||||
if (src && (this.playing || this.tracks.length) && !options.immediate) return this.addTrack(src);
|
if (src && (this.playing || this.tracks.length) && !options.immediate) return this.addTrack(src);
|
||||||
const track = options.filtersUpdate && !options.immediate ? src || this.current : src ?? this.tracks.shift();
|
const track = options.filtersUpdate && !options.immediate ? src || this.current : src ?? this.tracks.shift();
|
||||||
|
@ -314,8 +406,7 @@ class Queue<T = unknown> {
|
||||||
this.previousTracks.push(track);
|
this.previousTracks.push(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream,
|
let stream;
|
||||||
pauseEvent = false;
|
|
||||||
if (["youtube", "spotify"].includes(track.raw.source)) {
|
if (["youtube", "spotify"].includes(track.raw.source)) {
|
||||||
if (track.raw.source === "spotify" && !track.raw.engine) {
|
if (track.raw.source === "spotify" && !track.raw.engine) {
|
||||||
track.raw.engine = await YouTube.search(`${track.author} ${track.title}`, { type: "video" })
|
track.raw.engine = await YouTube.search(`${track.author} ${track.title}`, { type: "video" })
|
||||||
|
@ -333,7 +424,6 @@ class Queue<T = unknown> {
|
||||||
encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters.create(this._activeFilters)] : [],
|
encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters.create(this._activeFilters)] : [],
|
||||||
seek: options.seek ? options.seek / 1000 : 0
|
seek: options.seek ? options.seek / 1000 : 0
|
||||||
}).on("error", (err) => {
|
}).on("error", (err) => {
|
||||||
pauseEvent = true;
|
|
||||||
return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
|
return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -348,7 +438,6 @@ class Queue<T = unknown> {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.on("error", (err) => {
|
.on("error", (err) => {
|
||||||
pauseEvent = true;
|
|
||||||
return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
|
return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -359,66 +448,45 @@ class Queue<T = unknown> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.seek) this._streamTime = options.seek;
|
if (options.seek) this._streamTime = options.seek;
|
||||||
|
this._filtersUpdate = options.filtersUpdate;
|
||||||
|
|
||||||
const dispatcher = await this.connection.playStream(resource);
|
this.connection.playStream(resource).then(() => {
|
||||||
dispatcher.setVolume(this.options.initialVolume);
|
this.connection.setVolume(this.options.initialVolume);
|
||||||
|
|
||||||
// need to use these events here
|
|
||||||
dispatcher.once("start", () => {
|
|
||||||
this.playing = true;
|
|
||||||
if (options.filtersUpdate || pauseEvent) return;
|
|
||||||
this.player.emit("trackStart", this, this.current);
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatcher.once("finish", async () => {
|
|
||||||
this.playing = false;
|
|
||||||
if (options.filtersUpdate || pauseEvent) return;
|
|
||||||
|
|
||||||
this._streamTime = 0;
|
|
||||||
|
|
||||||
if (!this.tracks.length && this.repeatMode === QueueRepeatMode.OFF) {
|
|
||||||
if (this.options.leaveOnEnd) this.destroy();
|
|
||||||
this.player.emit("queueEnd", this);
|
|
||||||
} else {
|
|
||||||
if (this.repeatMode !== QueueRepeatMode.AUTOPLAY) {
|
|
||||||
if (this.repeatMode === QueueRepeatMode.TRACK) return void this.play(Util.last(this.previousTracks), { immediate: true });
|
|
||||||
if (this.repeatMode === QueueRepeatMode.QUEUE) this.tracks.push(Util.last(this.previousTracks));
|
|
||||||
const nextTrack = this.tracks.shift();
|
|
||||||
this.play(nextTrack, { immediate: true });
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
if (![track.source, track.raw?.source].includes("youtube")) {
|
|
||||||
if (this.options.leaveOnEnd) this.destroy();
|
|
||||||
return void this.player.emit("queueEnd", this);
|
|
||||||
}
|
|
||||||
const info = await ytdl
|
|
||||||
.getInfo(track.url)
|
|
||||||
.then((x) => x.related_videos[0])
|
|
||||||
.catch(() => {});
|
|
||||||
if (!info) {
|
|
||||||
if (this.options.leaveOnEnd) this.destroy();
|
|
||||||
return void this.player.emit("queueEnd", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextTrack = new Track(this.player, {
|
|
||||||
title: info.title,
|
|
||||||
url: `https://www.youtube.com/watch?v=${info.id}`,
|
|
||||||
duration: info.length_seconds ? Util.buildTimeCode(Util.parseMS(info.length_seconds * 1000)) : "0:00",
|
|
||||||
description: "",
|
|
||||||
thumbnail: Util.last(info.thumbnails).url,
|
|
||||||
views: parseInt(info.view_count.replace(/[^0-9]/g, "")),
|
|
||||||
author: typeof info.author === "string" ? info.author : info.author.name,
|
|
||||||
requestedBy: track.requestedBy,
|
|
||||||
source: "youtube"
|
|
||||||
});
|
|
||||||
|
|
||||||
this.play(nextTrack, { immediate: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _handleAutoplay(track: Track): Promise<void> {
|
||||||
|
this.#watchDestroyed();
|
||||||
|
if (!track || ![track.source, track.raw?.source].includes("youtube")) {
|
||||||
|
if (this.options.leaveOnEnd) this.destroy();
|
||||||
|
return void this.player.emit("queueEnd", this);
|
||||||
|
}
|
||||||
|
const info = await ytdl
|
||||||
|
.getInfo(track.url)
|
||||||
|
.then((x) => x.related_videos[0])
|
||||||
|
.catch(() => {});
|
||||||
|
if (!info) {
|
||||||
|
if (this.options.leaveOnEnd) this.destroy();
|
||||||
|
return void this.player.emit("queueEnd", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextTrack = new Track(this.player, {
|
||||||
|
title: info.title,
|
||||||
|
url: `https://www.youtube.com/watch?v=${info.id}`,
|
||||||
|
duration: info.length_seconds ? Util.buildTimeCode(Util.parseMS(info.length_seconds * 1000)) : "0:00",
|
||||||
|
description: "",
|
||||||
|
thumbnail: Util.last(info.thumbnails).url,
|
||||||
|
views: parseInt(info.view_count.replace(/[^0-9]/g, "")),
|
||||||
|
author: typeof info.author === "string" ? info.author : info.author.name,
|
||||||
|
requestedBy: track.requestedBy,
|
||||||
|
source: "youtube"
|
||||||
|
});
|
||||||
|
|
||||||
|
this.play(nextTrack, { immediate: true });
|
||||||
|
}
|
||||||
|
|
||||||
*[Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
|
this.#watchDestroyed();
|
||||||
yield* this.tracks;
|
yield* this.tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +495,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
toJSON() {
|
toJSON() {
|
||||||
|
this.#watchDestroyed();
|
||||||
return {
|
return {
|
||||||
guild: this.guild.id,
|
guild: this.guild.id,
|
||||||
voiceChannel: this.connection?.channel?.id,
|
voiceChannel: this.connection?.channel?.id,
|
||||||
|
@ -440,9 +509,14 @@ class Queue<T = unknown> {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
toString() {
|
toString() {
|
||||||
|
this.#watchDestroyed();
|
||||||
if (!this.tracks.length) return "No songs available to display!";
|
if (!this.tracks.length) return "No songs available to display!";
|
||||||
return `**Upcoming Songs:**\n${this.tracks.map((m, i) => `${i + 1}. **${m.title}**`).join("\n")}`;
|
return `**Upcoming Songs:**\n${this.tracks.map((m, i) => `${i + 1}. **${m.title}**`).join("\n")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#watchDestroyed() {
|
||||||
|
if (this.destroyed) throw new Error("Cannot use destroyed queue");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Queue };
|
export { Queue };
|
||||||
|
|
|
@ -132,6 +132,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
|
||||||
*/
|
*/
|
||||||
disconnect() {
|
disconnect() {
|
||||||
try {
|
try {
|
||||||
|
this.audioPlayer.stop(true);
|
||||||
this.voiceConnection.destroy();
|
this.voiceConnection.destroy();
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,6 +265,13 @@ export enum QueryType {
|
||||||
* @param {Error} error The error
|
* @param {Error} error The error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted on connection error. Sometimes stream errors are emitted here as well.
|
||||||
|
* @event Player#connectionError
|
||||||
|
* @param {Queue} queue The queue
|
||||||
|
* @param {Error} error The error
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when queue ends
|
* Emitted when queue ends
|
||||||
* @event Player#queueEnd
|
* @event Player#queueEnd
|
||||||
|
@ -298,6 +305,7 @@ export interface PlayerEvents {
|
||||||
connectionCreate: (queue: Queue, connection: StreamDispatcher) => any;
|
connectionCreate: (queue: Queue, connection: StreamDispatcher) => any;
|
||||||
debug: (queue: Queue, message: string) => any;
|
debug: (queue: Queue, message: string) => any;
|
||||||
error: (queue: Queue, error: Error) => any;
|
error: (queue: Queue, error: Error) => any;
|
||||||
|
connectionError: (queue: Queue, error: Error) => any;
|
||||||
queueEnd: (queue: Queue) => any;
|
queueEnd: (queue: Queue) => any;
|
||||||
trackAdd: (queue: Queue, track: Track) => any;
|
trackAdd: (queue: Queue, track: Track) => any;
|
||||||
tracksAdd: (queue: Queue, track: Track[]) => any;
|
tracksAdd: (queue: Queue, track: Track[]) => any;
|
||||||
|
|
Loading…
Reference in a new issue