feat: create PlayerError
This commit is contained in:
parent
8e00bca2df
commit
59b1c60440
5 changed files with 106 additions and 49 deletions
|
@ -8,6 +8,7 @@ import { QueryResolver } from "./utils/QueryResolver";
|
||||||
import YouTube from "youtube-sr";
|
import YouTube from "youtube-sr";
|
||||||
import { Util } from "./utils/Util";
|
import { Util } from "./utils/Util";
|
||||||
import Spotify from "spotify-url-info";
|
import Spotify from "spotify-url-info";
|
||||||
|
import { PlayerError, ErrorStatusCode } from "./Structures/PlayerError";
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Client as SoundCloud } from "soundcloud-scraper";
|
import { Client as SoundCloud } from "soundcloud-scraper";
|
||||||
|
@ -128,7 +129,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
*/
|
*/
|
||||||
createQueue<T = unknown>(guild: GuildResolvable, queueInitOptions: PlayerOptions & { metadata?: T } = {}): Queue<T> {
|
createQueue<T = unknown>(guild: GuildResolvable, queueInitOptions: PlayerOptions & { metadata?: T } = {}): Queue<T> {
|
||||||
guild = this.client.guilds.resolve(guild);
|
guild = this.client.guilds.resolve(guild);
|
||||||
if (!guild) throw new Error("Unknown Guild");
|
if (!guild) throw new PlayerError("Unknown Guild", ErrorStatusCode.UNKNOWN_GUILD);
|
||||||
if (this.queues.has(guild.id)) return this.queues.get(guild.id) as Queue<T>;
|
if (this.queues.has(guild.id)) return this.queues.get(guild.id) as Queue<T>;
|
||||||
|
|
||||||
const _meta = queueInitOptions.metadata;
|
const _meta = queueInitOptions.metadata;
|
||||||
|
@ -148,7 +149,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
*/
|
*/
|
||||||
getQueue<T = unknown>(guild: GuildResolvable) {
|
getQueue<T = unknown>(guild: GuildResolvable) {
|
||||||
guild = this.client.guilds.resolve(guild);
|
guild = this.client.guilds.resolve(guild);
|
||||||
if (!guild) throw new Error("Unknown Guild");
|
if (!guild) throw new PlayerError("Unknown Guild", ErrorStatusCode.UNKNOWN_GUILD);
|
||||||
return this.queues.get(guild.id) as Queue<T>;
|
return this.queues.get(guild.id) as Queue<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +160,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
*/
|
*/
|
||||||
deleteQueue<T = unknown>(guild: GuildResolvable) {
|
deleteQueue<T = unknown>(guild: GuildResolvable) {
|
||||||
guild = this.client.guilds.resolve(guild);
|
guild = this.client.guilds.resolve(guild);
|
||||||
if (!guild) throw new Error("Unknown Guild");
|
if (!guild) throw new PlayerError("Unknown Guild", ErrorStatusCode.UNKNOWN_GUILD);
|
||||||
const prev = this.getQueue<T>(guild);
|
const prev = this.getQueue<T>(guild);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -183,7 +184,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
*/
|
*/
|
||||||
async search(query: string | Track, options: SearchOptions) {
|
async search(query: string | Track, options: SearchOptions) {
|
||||||
if (query instanceof Track) return { playlist: null, tracks: [query] };
|
if (query instanceof Track) return { playlist: null, tracks: [query] };
|
||||||
if (!options) throw new Error("DiscordPlayer#search needs search options!");
|
if (!options) throw new PlayerError("DiscordPlayer#search needs search options!", ErrorStatusCode.INVALID_ARG_TYPE);
|
||||||
options.requestedBy = this.client.users.resolve(options.requestedBy);
|
options.requestedBy = this.client.users.resolve(options.requestedBy);
|
||||||
if (!("searchEngine" in options)) options.searchEngine = QueryType.AUTO;
|
if (!("searchEngine" in options)) options.searchEngine = QueryType.AUTO;
|
||||||
|
|
||||||
|
@ -451,7 +452,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
use(extractorName: string, extractor: ExtractorModel | any, force = false): ExtractorModel {
|
use(extractorName: string, extractor: ExtractorModel | any, force = false): ExtractorModel {
|
||||||
if (!extractorName) throw new Error("Cannot use unknown extractor!");
|
if (!extractorName) throw new PlayerError("Cannot use unknown extractor!", ErrorStatusCode.UNKNOWN_EXTRACTOR);
|
||||||
if (this.extractors.has(extractorName) && !force) return this.extractors.get(extractorName);
|
if (this.extractors.has(extractorName) && !force) return this.extractors.get(extractorName);
|
||||||
if (extractor instanceof ExtractorModel) {
|
if (extractor instanceof ExtractorModel) {
|
||||||
this.extractors.set(extractorName, extractor);
|
this.extractors.set(extractorName, extractor);
|
||||||
|
@ -459,7 +460,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const method of ["validate", "getInfo"]) {
|
for (const method of ["validate", "getInfo"]) {
|
||||||
if (typeof extractor[method] !== "function") throw new Error("Invalid extractor data!");
|
if (typeof extractor[method] !== "function") throw new PlayerError("Invalid extractor data!", ErrorStatusCode.INVALID_EXTRACTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const model = new ExtractorModel(extractorName, extractor);
|
const model = new ExtractorModel(extractorName, extractor);
|
||||||
|
@ -474,7 +475,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
* @returns {ExtractorModel}
|
* @returns {ExtractorModel}
|
||||||
*/
|
*/
|
||||||
unuse(extractorName: string) {
|
unuse(extractorName: string) {
|
||||||
if (!this.extractors.has(extractorName)) throw new Error(`Cannot find extractor "${extractorName}"`);
|
if (!this.extractors.has(extractorName)) throw new PlayerError(`Cannot find extractor "${extractorName}"`, ErrorStatusCode.UNKNOWN_EXTRACTOR);
|
||||||
const prev = this.extractors.get(extractorName);
|
const prev = this.extractors.get(extractorName);
|
||||||
this.extractors.delete(extractorName);
|
this.extractors.delete(extractorName);
|
||||||
return prev;
|
return prev;
|
||||||
|
|
48
src/Structures/PlayerError.ts
Normal file
48
src/Structures/PlayerError.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
export enum ErrorStatusCode {
|
||||||
|
STREAM_ERROR = "StreamError",
|
||||||
|
AUDIO_PLAYER_ERROR = "AudioPlayerError",
|
||||||
|
PLAYER_ERROR = "PlayerError",
|
||||||
|
NO_AUDIO_RESOURCE = "NoAudioResource",
|
||||||
|
UNKNOWN_GUILD = "UnknownGuild",
|
||||||
|
INVALID_ARG_TYPE = "InvalidArgType",
|
||||||
|
UNKNOWN_EXTRACTOR = "UnknownExtractor",
|
||||||
|
INVALID_EXTRACTOR = "InvalidExtractor",
|
||||||
|
INVALID_CHANNEL_TYPE = "InvalidChannelType",
|
||||||
|
INVALID_TRACK = "InvalidTrack",
|
||||||
|
UNKNOWN_REPEAT_MODE = "UnknownRepeatMode",
|
||||||
|
TRACK_NOT_FOUND = "TrackNotFound",
|
||||||
|
NO_CONNECTION = "NoConnection",
|
||||||
|
DESTROYED_QUEUE = "DestroyedQueue"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlayerError extends Error {
|
||||||
|
message: string;
|
||||||
|
statusCode: ErrorStatusCode;
|
||||||
|
createdAt = new Date();
|
||||||
|
|
||||||
|
constructor(message: string, code: ErrorStatusCode = ErrorStatusCode.PLAYER_ERROR) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.message = `[${code}] ${message}`;
|
||||||
|
this.statusCode = code;
|
||||||
|
this.name = code;
|
||||||
|
|
||||||
|
Error.captureStackTrace(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get createdTimestamp() {
|
||||||
|
return this.createdAt.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
valueOf() {
|
||||||
|
return this.statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return { stack: this.stack, code: this.statusCode, created: this.createdTimestamp };
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.stack;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import { AudioResource, StreamType } from "@discordjs/voice";
|
||||||
import { Util } from "../utils/Util";
|
import { Util } from "../utils/Util";
|
||||||
import YouTube from "youtube-sr";
|
import YouTube from "youtube-sr";
|
||||||
import AudioFilters from "../utils/AudioFilters";
|
import AudioFilters from "../utils/AudioFilters";
|
||||||
|
import { PlayerError, ErrorStatusCode } from "./PlayerError";
|
||||||
|
|
||||||
class Queue<T = unknown> {
|
class Queue<T = unknown> {
|
||||||
public readonly guild: Guild;
|
public readonly guild: Guild;
|
||||||
|
@ -113,7 +114,7 @@ class Queue<T = unknown> {
|
||||||
* @type {Track}
|
* @type {Track}
|
||||||
*/
|
*/
|
||||||
get current() {
|
get current() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return this.connection.audioResource?.metadata ?? this.tracks[0];
|
return this.connection.audioResource?.metadata ?? this.tracks[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {Track}
|
* @returns {Track}
|
||||||
*/
|
*/
|
||||||
nowPlaying() {
|
nowPlaying() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return this.current;
|
return this.current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,9 +141,10 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<Queue>}
|
* @returns {Promise<Queue>}
|
||||||
*/
|
*/
|
||||||
async connect(channel: GuildChannelResolvable) {
|
async connect(channel: GuildChannelResolvable) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
const _channel = this.guild.channels.resolve(channel) as StageChannel | VoiceChannel;
|
const _channel = this.guild.channels.resolve(channel) as StageChannel | VoiceChannel;
|
||||||
if (!["GUILD_STAGE_VOICE", "GUILD_VOICE"].includes(_channel?.type)) throw new TypeError(`Channel type must be GUILD_VOICE or GUILD_STAGE_VOICE, got ${_channel?.type}!`);
|
if (!["GUILD_STAGE_VOICE", "GUILD_VOICE"].includes(_channel?.type))
|
||||||
|
throw new PlayerError(`Channel type must be GUILD_VOICE or GUILD_STAGE_VOICE, got ${_channel?.type}!`, ErrorStatusCode.INVALID_ARG_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
|
||||||
});
|
});
|
||||||
|
@ -198,7 +200,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
destroy(disconnect = this.options.leaveOnStop) {
|
destroy(disconnect = this.options.leaveOnStop) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (this.connection) this.connection.end();
|
if (this.connection) 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);
|
||||||
|
@ -211,7 +213,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
skip() {
|
skip() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!this.connection) return false;
|
if (!this.connection) return false;
|
||||||
this._filtersUpdate = false;
|
this._filtersUpdate = false;
|
||||||
this.connection.end();
|
this.connection.end();
|
||||||
|
@ -224,8 +226,8 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
addTrack(track: Track) {
|
addTrack(track: Track) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!(track instanceof Track)) throw new Error("invalid track");
|
if (!(track instanceof Track)) throw new PlayerError("invalid track", ErrorStatusCode.INVALID_TRACK);
|
||||||
this.tracks.push(track);
|
this.tracks.push(track);
|
||||||
this.player.emit("trackAdd", this, track);
|
this.player.emit("trackAdd", this, track);
|
||||||
}
|
}
|
||||||
|
@ -235,8 +237,8 @@ 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();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!tracks.every((y) => y instanceof Track)) throw new Error("invalid track");
|
if (!tracks.every((y) => y instanceof Track)) throw new PlayerError("invalid track", ErrorStatusCode.INVALID_TRACK);
|
||||||
this.tracks.push(...tracks);
|
this.tracks.push(...tracks);
|
||||||
this.player.emit("tracksAdd", this, tracks);
|
this.player.emit("tracksAdd", this, tracks);
|
||||||
}
|
}
|
||||||
|
@ -247,7 +249,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
setPaused(paused?: boolean) {
|
setPaused(paused?: boolean) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
@ -258,7 +260,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
setBitrate(bitrate: number | "auto") {
|
setBitrate(bitrate: number | "auto") {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
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);
|
||||||
|
@ -270,7 +272,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
setVolume(amount: number) {
|
setVolume(amount: number) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!this.connection) return false;
|
if (!this.connection) return false;
|
||||||
this.#lastVolume = amount;
|
this.#lastVolume = amount;
|
||||||
this.options.initialVolume = amount;
|
this.options.initialVolume = amount;
|
||||||
|
@ -282,8 +284,9 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
setRepeatMode(mode: QueueRepeatMode) {
|
setRepeatMode(mode: QueueRepeatMode) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
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 PlayerError(`Unknown repeat mode "${mode}"!`, ErrorStatusCode.UNKNOWN_REPEAT_MODE);
|
||||||
if (mode === this.repeatMode) return false;
|
if (mode === this.repeatMode) return false;
|
||||||
this.repeatMode = mode;
|
this.repeatMode = mode;
|
||||||
return true;
|
return true;
|
||||||
|
@ -294,7 +297,7 @@ class Queue<T = unknown> {
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
get volume() {
|
get volume() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!this.connection) return 100;
|
if (!this.connection) return 100;
|
||||||
return this.connection.volume;
|
return this.connection.volume;
|
||||||
}
|
}
|
||||||
|
@ -326,7 +329,7 @@ class Queue<T = unknown> {
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
get streamTime() {
|
get streamTime() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
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;
|
||||||
|
@ -337,7 +340,7 @@ class Queue<T = unknown> {
|
||||||
}
|
}
|
||||||
|
|
||||||
set streamTime(time: number) {
|
set streamTime(time: number) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
this.seek(time);
|
this.seek(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +349,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {AudioFilters}
|
* @returns {AudioFilters}
|
||||||
*/
|
*/
|
||||||
getFiltersEnabled() {
|
getFiltersEnabled() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return AudioFilters.names.filter((x) => this._activeFilters.includes(x));
|
return AudioFilters.names.filter((x) => this._activeFilters.includes(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +358,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {AudioFilters}
|
* @returns {AudioFilters}
|
||||||
*/
|
*/
|
||||||
getFiltersDisabled() {
|
getFiltersDisabled() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return AudioFilters.names.filter((x) => !this._activeFilters.includes(x));
|
return AudioFilters.names.filter((x) => !this._activeFilters.includes(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +368,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async setFilters(filters?: QueueFilters) {
|
async setFilters(filters?: QueueFilters) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!filters || !Object.keys(filters).length) {
|
if (!filters || !Object.keys(filters).length) {
|
||||||
// reset filters
|
// reset filters
|
||||||
const streamTime = this.streamTime;
|
const streamTime = this.streamTime;
|
||||||
|
@ -404,7 +407,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
async seek(position: number) {
|
async seek(position: number) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
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();
|
||||||
|
@ -423,9 +426,9 @@ class Queue<T = unknown> {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async back() {
|
async back() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
const prev = this.previousTracks[this.previousTracks.length - 2]; // because last item is the current track
|
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");
|
if (!prev) throw new PlayerError("Could not find previous track", ErrorStatusCode.TRACK_NOT_FOUND);
|
||||||
|
|
||||||
return await this.play(prev, { immediate: true });
|
return await this.play(prev, { immediate: true });
|
||||||
}
|
}
|
||||||
|
@ -434,7 +437,7 @@ class Queue<T = unknown> {
|
||||||
* Clear this queue
|
* Clear this queue
|
||||||
*/
|
*/
|
||||||
clear() {
|
clear() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
this.tracks = [];
|
this.tracks = [];
|
||||||
this.previousTracks = [];
|
this.previousTracks = [];
|
||||||
}
|
}
|
||||||
|
@ -444,7 +447,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
stop() {
|
stop() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return this.destroy();
|
return this.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +456,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
shuffle() {
|
shuffle() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!this.tracks.length || this.tracks.length < 3) return false;
|
if (!this.tracks.length || this.tracks.length < 3) return false;
|
||||||
const currentTrack = this.tracks.shift();
|
const currentTrack = this.tracks.shift();
|
||||||
|
|
||||||
|
@ -473,7 +476,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {Track}
|
* @returns {Track}
|
||||||
*/
|
*/
|
||||||
remove(track: Track | Snowflake | number) {
|
remove(track: Track | Snowflake | number) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
let trackFound: Track = null;
|
let trackFound: Track = null;
|
||||||
if (typeof track === "number") {
|
if (typeof track === "number") {
|
||||||
trackFound = this.tracks[track];
|
trackFound = this.tracks[track];
|
||||||
|
@ -496,9 +499,9 @@ class Queue<T = unknown> {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
jump(track: Track | number): void {
|
jump(track: Track | number): void {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
const foundTrack = this.remove(track);
|
const foundTrack = this.remove(track);
|
||||||
if (!foundTrack) throw new Error("Track not found");
|
if (!foundTrack) throw new PlayerError("Track not found", ErrorStatusCode.TRACK_NOT_FOUND);
|
||||||
this.tracks.splice(1, 0, foundTrack);
|
this.tracks.splice(1, 0, foundTrack);
|
||||||
|
|
||||||
return void this.skip();
|
return void this.skip();
|
||||||
|
@ -510,8 +513,8 @@ class Queue<T = unknown> {
|
||||||
* @param {number} [index=0] The index where this track should be
|
* @param {number} [index=0] The index where this track should be
|
||||||
*/
|
*/
|
||||||
insert(track: Track, index = 0) {
|
insert(track: Track, index = 0) {
|
||||||
if (!track || !(track instanceof Track)) throw new TypeError("track must be the instance of Track");
|
if (!track || !(track instanceof Track)) throw new PlayerError("track must be the instance of Track", ErrorStatusCode.INVALID_TRACK);
|
||||||
if (typeof index !== "number" || index < 0 || !Number.isFinite(index)) throw new Error(`Invalid index "${index}"`);
|
if (typeof index !== "number" || index < 0 || !Number.isFinite(index)) throw new PlayerError(`Invalid index "${index}"`, ErrorStatusCode.INVALID_ARG_TYPE);
|
||||||
|
|
||||||
this.tracks.splice(index, 0, track);
|
this.tracks.splice(index, 0, track);
|
||||||
|
|
||||||
|
@ -530,7 +533,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {PlayerTimestamp}
|
* @returns {PlayerTimestamp}
|
||||||
*/
|
*/
|
||||||
getPlayerTimestamp() {
|
getPlayerTimestamp() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
const currentStreamTime = this.streamTime;
|
const currentStreamTime = this.streamTime;
|
||||||
const totalTime = this.current.durationMS;
|
const totalTime = this.current.durationMS;
|
||||||
|
|
||||||
|
@ -550,7 +553,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
createProgressBar(options: PlayerProgressbarOptions = { timecodes: true }) {
|
createProgressBar(options: PlayerProgressbarOptions = { timecodes: true }) {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
const length = typeof options.length === "number" ? (options.length <= 0 || options.length === Infinity ? 15 : options.length) : 15;
|
const length = typeof options.length === "number" ? (options.length <= 0 || options.length === Infinity ? 15 : options.length) : 15;
|
||||||
|
|
||||||
const index = Math.round((this.streamTime / this.current.durationMS) * length);
|
const index = Math.round((this.streamTime / this.current.durationMS) * length);
|
||||||
|
@ -581,7 +584,7 @@ class Queue<T = unknown> {
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
*/
|
*/
|
||||||
get totalTime(): number {
|
get totalTime(): number {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
|
return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +596,7 @@ class Queue<T = unknown> {
|
||||||
*/
|
*/
|
||||||
async play(src?: Track, options: PlayOptions = {}): Promise<void> {
|
async play(src?: Track, options: PlayOptions = {}): Promise<void> {
|
||||||
if (!this.destroyed) this.#watchDestroyed();
|
if (!this.destroyed) 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 PlayerError("Voice connection is not available, use <Queue>.connect()!", ErrorStatusCode.NO_CONNECTION);
|
||||||
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();
|
||||||
if (!track) return;
|
if (!track) return;
|
||||||
|
@ -663,7 +666,7 @@ class Queue<T = unknown> {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private async _handleAutoplay(track: Track): Promise<void> {
|
private async _handleAutoplay(track: Track): Promise<void> {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
if (!track || ![track.source, track.raw?.source].includes("youtube")) {
|
if (!track || ![track.source, track.raw?.source].includes("youtube")) {
|
||||||
if (this.options.leaveOnEnd) this.destroy();
|
if (this.options.leaveOnEnd) this.destroy();
|
||||||
return void this.player.emit("queueEnd", this);
|
return void this.player.emit("queueEnd", this);
|
||||||
|
@ -692,7 +695,7 @@ class Queue<T = unknown> {
|
||||||
}
|
}
|
||||||
|
|
||||||
*[Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
yield* this.tracks;
|
yield* this.tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,7 +704,7 @@ class Queue<T = unknown> {
|
||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
toJSON() {
|
toJSON() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
guild: this.guild.id,
|
guild: this.guild.id,
|
||||||
|
@ -716,13 +719,16 @@ class Queue<T = unknown> {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
toString() {
|
toString() {
|
||||||
this.#watchDestroyed();
|
if (this.#watchDestroyed()) return;
|
||||||
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() {
|
#watchDestroyed() {
|
||||||
if (this.#destroyed) throw new Error("Cannot use destroyed queue");
|
if (this.#destroyed) {
|
||||||
|
this.player.emit("error", this, new PlayerError("Cannot use destroyed queue", ErrorStatusCode.DESTROYED_QUEUE));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#getBufferingTimeout() {
|
#getBufferingTimeout() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { Duplex, Readable } from "stream";
|
||||||
import { TypedEmitter as EventEmitter } from "tiny-typed-emitter";
|
import { TypedEmitter as EventEmitter } from "tiny-typed-emitter";
|
||||||
import Track from "../Structures/Track";
|
import Track from "../Structures/Track";
|
||||||
import { Util } from "../utils/Util";
|
import { Util } from "../utils/Util";
|
||||||
|
import { PlayerError, ErrorStatusCode } from "../Structures/PlayerError";
|
||||||
|
|
||||||
export interface VoiceEvents {
|
export interface VoiceEvents {
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
@ -182,7 +183,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
|
||||||
* @returns {Promise<StreamDispatcher>}
|
* @returns {Promise<StreamDispatcher>}
|
||||||
*/
|
*/
|
||||||
async playStream(resource: AudioResource<Track> = this.audioResource) {
|
async playStream(resource: AudioResource<Track> = this.audioResource) {
|
||||||
if (!resource) throw new Error("Audio resource is not available!");
|
if (!resource) throw new PlayerError("Audio resource is not available!", ErrorStatusCode.NO_AUDIO_RESOURCE);
|
||||||
if (!this.audioResource) this.audioResource = resource;
|
if (!this.audioResource) this.audioResource = resource;
|
||||||
if (this.voiceConnection.state.status !== VoiceConnectionStatus.Ready) await entersState(this.voiceConnection, VoiceConnectionStatus.Ready, 20000);
|
if (this.voiceConnection.state.status !== VoiceConnectionStatus.Ready) await entersState(this.voiceConnection, VoiceConnectionStatus.Ready, 20000);
|
||||||
this.audioPlayer.play(resource);
|
this.audioPlayer.play(resource);
|
||||||
|
|
|
@ -2,6 +2,7 @@ export { AudioFilters } from "./utils/AudioFilters";
|
||||||
export { ExtractorModel } from "./Structures/ExtractorModel";
|
export { ExtractorModel } from "./Structures/ExtractorModel";
|
||||||
export { Playlist } from "./Structures/Playlist";
|
export { Playlist } from "./Structures/Playlist";
|
||||||
export { Player } from "./Player";
|
export { Player } from "./Player";
|
||||||
|
export { PlayerError, ErrorStatusCode } from "./Structures/PlayerError";
|
||||||
export { QueryResolver } from "./utils/QueryResolver";
|
export { QueryResolver } from "./utils/QueryResolver";
|
||||||
export { Queue } from "./Structures/Queue";
|
export { Queue } from "./Structures/Queue";
|
||||||
export { Track } from "./Structures/Track";
|
export { Track } from "./Structures/Track";
|
||||||
|
|
Loading…
Reference in a new issue