discord-player 5.3.0-dev.1 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,789 +1,769 @@
1
- "use strict";
2
- var _Queue_instances, _Queue_lastVolume, _Queue_destroyed, _Queue_watchDestroyed, _Queue_getBufferingTimeout;
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.Queue = void 0;
5
- const tslib_1 = require("tslib");
6
- const discord_js_1 = require("discord.js");
7
- const Track_1 = tslib_1.__importDefault(require("./Track"));
8
- const types_1 = require("../types/types");
9
- const discord_ytdl_core_1 = tslib_1.__importDefault(require("discord-ytdl-core"));
10
- const voice_1 = require("@discordjs/voice");
11
- const Util_1 = require("../utils/Util");
12
- const youtube_sr_1 = tslib_1.__importDefault(require("youtube-sr"));
13
- const AudioFilters_1 = tslib_1.__importDefault(require("../utils/AudioFilters"));
14
- const PlayerError_1 = require("./PlayerError");
15
- class Queue {
16
- /**
17
- * Queue constructor
18
- * @param {Player} player The player that instantiated this queue
19
- * @param {Guild} guild The guild that instantiated this queue
20
- * @param {PlayerOptions} [options={}] Player options for the queue
21
- */
22
- constructor(player, guild, options = {}) {
23
- _Queue_instances.add(this);
24
- this.tracks = [];
25
- this.previousTracks = [];
26
- this.playing = false;
27
- this.metadata = null;
28
- this.repeatMode = 0;
29
- this.id = discord_js_1.SnowflakeUtil.generate().toString();
30
- this._streamTime = 0;
31
- this._cooldownsTimeout = new discord_js_1.Collection();
32
- this._activeFilters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
33
- this._filtersUpdate = false;
34
- _Queue_lastVolume.set(this, 0);
35
- _Queue_destroyed.set(this, false);
36
- this.onBeforeCreateStream = null;
37
- /**
38
- * The player that instantiated this queue
39
- * @type {Player}
40
- * @readonly
41
- */
42
- this.player = player;
43
- /**
44
- * The guild that instantiated this queue
45
- * @type {Guild}
46
- * @readonly
47
- */
48
- this.guild = guild;
49
- /**
50
- * The player options for this queue
51
- * @type {PlayerOptions}
52
- */
53
- this.options = {};
54
- /**
55
- * Queue repeat mode
56
- * @type {QueueRepeatMode}
57
- * @name Queue#repeatMode
58
- */
59
- /**
60
- * Queue metadata
61
- * @type {any}
62
- * @name Queue#metadata
63
- */
64
- /**
65
- * Previous tracks
66
- * @type {Track[]}
67
- * @name Queue#previousTracks
68
- */
69
- /**
70
- * Regular tracks
71
- * @type {Track[]}
72
- * @name Queue#tracks
73
- */
74
- /**
75
- * The connection
76
- * @type {StreamDispatcher}
77
- * @name Queue#connection
78
- */
79
- /**
80
- * The ID of this queue
81
- * @type {Snowflake}
82
- * @name Queue#id
83
- */
84
- Object.assign(this.options, {
85
- leaveOnEnd: true,
86
- leaveOnStop: true,
87
- leaveOnEmpty: true,
88
- leaveOnEmptyCooldown: 1000,
89
- autoSelfDeaf: true,
90
- ytdlOptions: {
91
- highWaterMark: 1 << 25
92
- },
93
- initialVolume: 100,
94
- bufferingTimeout: 3000,
95
- spotifyBridge: true,
96
- disableVolume: false
97
- }, options);
98
- if ("onBeforeCreateStream" in this.options)
99
- this.onBeforeCreateStream = this.options.onBeforeCreateStream;
100
- this.player.emit("debug", this, `Queue initialized:\n\n${this.player.scanDeps()}`);
101
- }
102
- /**
103
- * Returns current track
104
- * @type {Track}
105
- */
106
- get current() {
107
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
108
- return;
109
- return this.connection.audioResource?.metadata ?? this.tracks[0];
110
- }
111
- /**
112
- * If this queue is destroyed
113
- * @type {boolean}
114
- */
115
- get destroyed() {
116
- return tslib_1.__classPrivateFieldGet(this, _Queue_destroyed, "f");
117
- }
118
- /**
119
- * Returns current track
120
- * @returns {Track}
121
- */
122
- nowPlaying() {
123
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
124
- return;
125
- return this.current;
126
- }
127
- /**
128
- * Connects to a voice channel
129
- * @param {GuildChannelResolvable} channel The voice/stage channel
130
- * @returns {Promise<Queue>}
131
- */
132
- async connect(channel) {
133
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
134
- return;
135
- const _channel = this.guild.channels.resolve(channel);
136
- if (![discord_js_1.ChannelType.GuildStageVoice, discord_js_1.ChannelType.GuildVoice].includes(_channel?.type))
137
- throw new PlayerError_1.PlayerError(`Channel type must be GuildVoice or GuildStageVoice, got ${_channel?.type}!`, PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
138
- const connection = await this.player.voiceUtils.connect(_channel, {
139
- deaf: this.options.autoSelfDeaf
140
- });
141
- this.connection = connection;
142
- if (_channel.type === discord_js_1.ChannelType.GuildStageVoice) {
143
- await _channel.guild.members.me.voice.setSuppressed(false).catch(async () => {
144
- return await _channel.guild.members.me.voice.setRequestToSpeak(true).catch(Util_1.Util.noop);
145
- });
146
- }
147
- this.connection.on("error", (err) => {
148
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
149
- return;
150
- this.player.emit("connectionError", this, err);
151
- });
152
- this.connection.on("debug", (msg) => {
153
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
154
- return;
155
- this.player.emit("debug", this, msg);
156
- });
157
- this.player.emit("connectionCreate", this, this.connection);
158
- this.connection.on("start", (resource) => {
159
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
160
- return;
161
- this.playing = true;
162
- if (!this._filtersUpdate)
163
- this.player.emit("trackStart", this, resource?.metadata ?? this.current);
164
- this._filtersUpdate = false;
165
- });
166
- this.connection.on("finish", async (resource) => {
167
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
168
- return;
169
- this.playing = false;
170
- if (this._filtersUpdate)
171
- return;
172
- this._streamTime = 0;
173
- if (resource?.metadata)
174
- this.previousTracks.push(resource.metadata);
175
- this.player.emit("trackEnd", this, resource.metadata);
176
- if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.OFF) {
177
- if (this.options.leaveOnEnd)
178
- this.destroy();
179
- this.player.emit("queueEnd", this);
180
- }
181
- else if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.AUTOPLAY) {
182
- this._handleAutoplay(Util_1.Util.last(this.previousTracks));
183
- }
184
- else {
185
- if (this.repeatMode === types_1.QueueRepeatMode.TRACK)
186
- return void this.play(Util_1.Util.last(this.previousTracks), { immediate: true });
187
- if (this.repeatMode === types_1.QueueRepeatMode.QUEUE)
188
- this.tracks.push(Util_1.Util.last(this.previousTracks));
189
- const nextTrack = this.tracks.shift();
190
- this.play(nextTrack, { immediate: true });
191
- return;
192
- }
193
- });
194
- await this.player.voiceUtils.enterReady(this.connection.voiceConnection, {
195
- maxTime: this.player.options.connectionTimeout || 30000
196
- });
197
- return this;
198
- }
199
- /**
200
- * Destroys this queue
201
- * @param {boolean} [disconnect=this.options.leaveOnStop] If it should leave on destroy
202
- * @returns {void}
203
- */
204
- destroy(disconnect = this.options.leaveOnStop) {
205
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
206
- return;
207
- if (this.connection)
208
- this.connection.end();
209
- if (disconnect)
210
- this.connection?.disconnect();
211
- this.player.queues.delete(this.guild.id);
212
- this.player.voiceUtils.cache.delete(this.guild.id);
213
- tslib_1.__classPrivateFieldSet(this, _Queue_destroyed, true, "f");
214
- }
215
- /**
216
- * Skips current track
217
- * @returns {boolean}
218
- */
219
- skip() {
220
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
221
- return;
222
- if (!this.connection)
223
- return false;
224
- this._filtersUpdate = false;
225
- this.connection.end();
226
- return true;
227
- }
228
- /**
229
- * Adds single track to the queue
230
- * @param {Track} track The track to add
231
- * @returns {void}
232
- */
233
- addTrack(track) {
234
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
235
- return;
236
- if (!(track instanceof Track_1.default))
237
- throw new PlayerError_1.PlayerError("invalid track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
238
- this.tracks.push(track);
239
- this.player.emit("trackAdd", this, track);
240
- }
241
- /**
242
- * Adds multiple tracks to the queue
243
- * @param {Track[]} tracks Array of tracks to add
244
- */
245
- addTracks(tracks) {
246
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
247
- return;
248
- if (!tracks.every((y) => y instanceof Track_1.default))
249
- throw new PlayerError_1.PlayerError("invalid track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
250
- this.tracks.push(...tracks);
251
- this.player.emit("tracksAdd", this, tracks);
252
- }
253
- /**
254
- * Sets paused state
255
- * @param {boolean} paused The paused state
256
- * @returns {boolean}
257
- */
258
- setPaused(paused) {
259
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
260
- return;
261
- if (!this.connection)
262
- return false;
263
- return paused ? this.connection.pause(true) : this.connection.resume();
264
- }
265
- /**
266
- * Sets bitrate
267
- * @param {number|auto} bitrate bitrate to set
268
- * @returns {void}
269
- */
270
- setBitrate(bitrate) {
271
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
272
- return;
273
- if (!this.connection?.audioResource?.encoder)
274
- return;
275
- if (bitrate === "auto")
276
- bitrate = this.connection.channel?.bitrate ?? 64000;
277
- this.connection.audioResource.encoder.setBitrate(bitrate);
278
- }
279
- /**
280
- * Sets volume
281
- * @param {number} amount The volume amount
282
- * @returns {boolean}
283
- */
284
- setVolume(amount) {
285
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
286
- return;
287
- if (!this.connection)
288
- return false;
289
- tslib_1.__classPrivateFieldSet(this, _Queue_lastVolume, amount, "f");
290
- this.options.initialVolume = amount;
291
- return this.connection.setVolume(amount);
292
- }
293
- /**
294
- * Sets repeat mode
295
- * @param {QueueRepeatMode} mode The repeat mode
296
- * @returns {boolean}
297
- */
298
- setRepeatMode(mode) {
299
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
300
- return;
301
- if (![types_1.QueueRepeatMode.OFF, types_1.QueueRepeatMode.QUEUE, types_1.QueueRepeatMode.TRACK, types_1.QueueRepeatMode.AUTOPLAY].includes(mode))
302
- throw new PlayerError_1.PlayerError(`Unknown repeat mode "${mode}"!`, PlayerError_1.ErrorStatusCode.UNKNOWN_REPEAT_MODE);
303
- if (mode === this.repeatMode)
304
- return false;
305
- this.repeatMode = mode;
306
- return true;
307
- }
308
- /**
309
- * The current volume amount
310
- * @type {number}
311
- */
312
- get volume() {
313
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
314
- return;
315
- if (!this.connection)
316
- return 100;
317
- return this.connection.volume;
318
- }
319
- set volume(amount) {
320
- this.setVolume(amount);
321
- }
322
- /**
323
- * The stream time of this queue
324
- * @type {number}
325
- */
326
- get streamTime() {
327
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
328
- return;
329
- if (!this.connection)
330
- return 0;
331
- const playbackTime = this._streamTime + this.connection.streamTime;
332
- const NC = this._activeFilters.includes("nightcore") ? 1.25 : null;
333
- const VW = this._activeFilters.includes("vaporwave") ? 0.8 : null;
334
- if (NC && VW)
335
- return playbackTime * (NC + VW);
336
- return NC ? playbackTime * NC : VW ? playbackTime * VW : playbackTime;
337
- }
338
- set streamTime(time) {
339
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
340
- return;
341
- this.seek(time);
342
- }
343
- /**
344
- * Returns enabled filters
345
- * @returns {AudioFilters}
346
- */
347
- getFiltersEnabled() {
348
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
349
- return;
350
- return AudioFilters_1.default.names.filter((x) => this._activeFilters.includes(x));
351
- }
352
- /**
353
- * Returns disabled filters
354
- * @returns {AudioFilters}
355
- */
356
- getFiltersDisabled() {
357
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
358
- return;
359
- return AudioFilters_1.default.names.filter((x) => !this._activeFilters.includes(x));
360
- }
361
- /**
362
- * Sets filters
363
- * @param {QueueFilters} filters Queue filters
364
- * @returns {Promise<void>}
365
- */
366
- async setFilters(filters) {
367
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
368
- return;
369
- if (!filters || !Object.keys(filters).length) {
370
- // reset filters
371
- const streamTime = this.streamTime;
372
- this._activeFilters = [];
373
- return await this.play(this.current, {
374
- immediate: true,
375
- filtersUpdate: true,
376
- seek: streamTime,
377
- encoderArgs: []
378
- });
379
- }
380
- const _filters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
381
- for (const filter in filters) {
382
- if (filters[filter] === true)
383
- _filters.push(filter);
384
- }
385
- if (this._activeFilters.join("") === _filters.join(""))
386
- return;
387
- const newFilters = AudioFilters_1.default.create(_filters).trim();
388
- const streamTime = this.streamTime;
389
- this._activeFilters = _filters;
390
- return await this.play(this.current, {
391
- immediate: true,
392
- filtersUpdate: true,
393
- seek: streamTime,
394
- encoderArgs: !_filters.length ? undefined : ["-af", newFilters]
395
- });
396
- }
397
- /**
398
- * Seeks to the given time
399
- * @param {number} position The position
400
- * @returns {boolean}
401
- */
402
- async seek(position) {
403
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
404
- return;
405
- if (!this.playing || !this.current)
406
- return false;
407
- if (position < 1)
408
- position = 0;
409
- if (position >= this.current.durationMS)
410
- return this.skip();
411
- await this.play(this.current, {
412
- immediate: true,
413
- filtersUpdate: true,
414
- seek: position
415
- });
416
- return true;
417
- }
418
- /**
419
- * Plays previous track
420
- * @returns {Promise<void>}
421
- */
422
- async back() {
423
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
424
- return;
425
- const prev = this.previousTracks[this.previousTracks.length - 2]; // because last item is the current track
426
- if (!prev)
427
- throw new PlayerError_1.PlayerError("Could not find previous track", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
428
- return await this.play(prev, { immediate: true });
429
- }
430
- /**
431
- * Clear this queue
432
- */
433
- clear() {
434
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
435
- return;
436
- this.tracks = [];
437
- this.previousTracks = [];
438
- }
439
- /**
440
- * Stops the player
441
- * @returns {void}
442
- */
443
- stop() {
444
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
445
- return;
446
- return this.destroy();
447
- }
448
- /**
449
- * Shuffles this queue
450
- * @returns {boolean}
451
- */
452
- shuffle() {
453
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
454
- return;
455
- if (!this.tracks.length || this.tracks.length < 3)
456
- return false;
457
- const currentTrack = this.tracks.shift();
458
- for (let i = this.tracks.length - 1; i > 0; i--) {
459
- const j = Math.floor(Math.random() * (i + 1));
460
- [this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
461
- }
462
- this.tracks.unshift(currentTrack);
463
- return true;
464
- }
465
- /**
466
- * Removes a track from the queue
467
- * @param {Track|string|number} track The track to remove
468
- * @returns {Track}
469
- */
470
- remove(track) {
471
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
472
- return;
473
- let trackFound = null;
474
- if (typeof track === "number") {
475
- trackFound = this.tracks[track];
476
- if (trackFound) {
477
- this.tracks = this.tracks.filter((t) => t.id !== trackFound.id);
478
- }
479
- }
480
- else {
481
- trackFound = this.tracks.find((s) => s.id === (track instanceof Track_1.default ? track.id : track));
482
- if (trackFound) {
483
- this.tracks = this.tracks.filter((s) => s.id !== trackFound.id);
484
- }
485
- }
486
- return trackFound;
487
- }
488
- /**
489
- * Returns the index of the specified track. If found, returns the track index else returns -1.
490
- * @param {number|Track|string} track The track
491
- * @returns {number}
492
- */
493
- getTrackPosition(track) {
494
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
495
- return;
496
- if (typeof track === "number")
497
- return this.tracks[track] != null ? track : -1;
498
- return this.tracks.findIndex((pred) => pred.id === (track instanceof Track_1.default ? track.id : track));
499
- }
500
- /**
501
- * Jumps to particular track
502
- * @param {Track|number} track The track
503
- * @returns {void}
504
- */
505
- jump(track) {
506
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
507
- return;
508
- const foundTrack = this.remove(track);
509
- if (!foundTrack)
510
- throw new PlayerError_1.PlayerError("Track not found", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
511
- this.tracks.splice(0, 0, foundTrack);
512
- return void this.skip();
513
- }
514
- /**
515
- * Jumps to particular track, removing other tracks on the way
516
- * @param {Track|number} track The track
517
- * @returns {void}
518
- */
519
- skipTo(track) {
520
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
521
- return;
522
- const trackIndex = this.getTrackPosition(track);
523
- const removedTrack = this.remove(track);
524
- if (!removedTrack)
525
- throw new PlayerError_1.PlayerError("Track not found", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
526
- this.tracks.splice(0, trackIndex, removedTrack);
527
- return void this.skip();
528
- }
529
- /**
530
- * Inserts the given track to specified index
531
- * @param {Track} track The track to insert
532
- * @param {number} [index=0] The index where this track should be
533
- */
534
- insert(track, index = 0) {
535
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
536
- return;
537
- if (!track || !(track instanceof Track_1.default))
538
- throw new PlayerError_1.PlayerError("track must be the instance of Track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
539
- if (typeof index !== "number" || index < 0 || !Number.isFinite(index))
540
- throw new PlayerError_1.PlayerError(`Invalid index "${index}"`, PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
541
- this.tracks.splice(index, 0, track);
542
- this.player.emit("trackAdd", this, track);
543
- }
544
- /**
545
- * @typedef {object} PlayerTimestamp
546
- * @property {string} current The current progress
547
- * @property {string} end The total time
548
- * @property {number} progress Progress in %
549
- */
550
- /**
551
- * Returns player stream timestamp
552
- * @returns {PlayerTimestamp}
553
- */
554
- getPlayerTimestamp() {
555
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
556
- return;
557
- const currentStreamTime = this.streamTime;
558
- const totalTime = this.current.durationMS;
559
- const currentTimecode = Util_1.Util.buildTimeCode(Util_1.Util.parseMS(currentStreamTime));
560
- const endTimecode = Util_1.Util.buildTimeCode(Util_1.Util.parseMS(totalTime));
561
- return {
562
- current: currentTimecode,
563
- end: endTimecode,
564
- progress: Math.round((currentStreamTime / totalTime) * 100)
565
- };
566
- }
567
- /**
568
- * Creates progress bar string
569
- * @param {PlayerProgressbarOptions} options The progress bar options
570
- * @returns {string}
571
- */
572
- createProgressBar(options = { timecodes: true }) {
573
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
574
- return;
575
- const length = typeof options.length === "number" ? (options.length <= 0 || options.length === Infinity ? 15 : options.length) : 15;
576
- const index = Math.round((this.streamTime / this.current.durationMS) * length);
577
- const indicator = typeof options.indicator === "string" && options.indicator.length > 0 ? options.indicator : "🔘";
578
- const line = typeof options.line === "string" && options.line.length > 0 ? options.line : "";
579
- if (index >= 1 && index <= length) {
580
- const bar = line.repeat(length - 1).split("");
581
- bar.splice(index, 0, indicator);
582
- if (options.timecodes) {
583
- const timestamp = this.getPlayerTimestamp();
584
- return `${timestamp.current} ┃ ${bar.join("")} ┃ ${timestamp.end}`;
585
- }
586
- else {
587
- return `${bar.join("")}`;
588
- }
589
- }
590
- else {
591
- if (options.timecodes) {
592
- const timestamp = this.getPlayerTimestamp();
593
- return `${timestamp.current} ┃ ${indicator}${line.repeat(length - 1)} ┃ ${timestamp.end}`;
594
- }
595
- else {
596
- return `${indicator}${line.repeat(length - 1)}`;
597
- }
598
- }
599
- }
600
- /**
601
- * Total duration
602
- * @type {Number}
603
- */
604
- get totalTime() {
605
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
606
- return;
607
- return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
608
- }
609
- /**
610
- * Play stream in a voice/stage channel
611
- * @param {Track} [src] The track to play (if empty, uses first track from the queue)
612
- * @param {PlayOptions} [options={}] The options
613
- * @returns {Promise<void>}
614
- */
615
- async play(src, options = {}) {
616
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
617
- return;
618
- if (!this.connection || !this.connection.voiceConnection)
619
- throw new PlayerError_1.PlayerError("Voice connection is not available, use <Queue>.connect()!", PlayerError_1.ErrorStatusCode.NO_CONNECTION);
620
- if (src && (this.playing || this.tracks.length) && !options.immediate)
621
- return this.addTrack(src);
622
- const track = options.filtersUpdate && !options.immediate ? src || this.current : src ?? this.tracks.shift();
623
- if (!track)
624
- return;
625
- this.player.emit("debug", this, "Received play request");
626
- if (!options.filtersUpdate) {
627
- this.previousTracks = this.previousTracks.filter((x) => x.id !== track.id);
628
- this.previousTracks.push(track);
629
- }
630
- let stream = null;
631
- const customDownloader = typeof this.onBeforeCreateStream === "function";
632
- if (["youtube", "spotify"].includes(track.raw.source)) {
633
- let spotifyResolved = false;
634
- if (this.options.spotifyBridge && track.raw.source === "spotify" && !track.raw.engine) {
635
- track.raw.engine = await youtube_sr_1.default.search(`${track.author} ${track.title}`, { type: "video" })
636
- .then((x) => x[0].url)
637
- .catch(() => null);
638
- spotifyResolved = true;
639
- }
640
- const link = track.raw.source === "spotify" ? track.raw.engine : track.url;
641
- if (!link)
642
- return void this.play(this.tracks.shift(), { immediate: true });
643
- if (customDownloader) {
644
- stream = (await this.onBeforeCreateStream(track, spotifyResolved ? "youtube" : track.raw.source, this)) ?? null;
645
- if (stream)
646
- stream = discord_ytdl_core_1.default
647
- .arbitraryStream(stream, {
648
- opusEncoded: false,
649
- fmt: "s16le",
650
- encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters_1.default.create(this._activeFilters)] : [],
651
- seek: options.seek ? options.seek / 1000 : 0
652
- })
653
- .on("error", (err) => {
654
- return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
655
- });
656
- }
657
- else {
658
- stream = (0, discord_ytdl_core_1.default)(link, {
659
- ...this.options.ytdlOptions,
660
- // discord-ytdl-core
661
- opusEncoded: false,
662
- fmt: "s16le",
663
- encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters_1.default.create(this._activeFilters)] : [],
664
- seek: options.seek ? options.seek / 1000 : 0
665
- }).on("error", (err) => {
666
- return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
667
- });
668
- }
669
- }
670
- else {
671
- const tryArb = (customDownloader && (await this.onBeforeCreateStream(track, track.raw.source || track.raw.engine, this))) || null;
672
- const arbitrarySource = tryArb
673
- ? tryArb
674
- : track.raw.source === "soundcloud"
675
- ? await track.raw.engine.downloadProgressive()
676
- : typeof track.raw.engine === "function"
677
- ? await track.raw.engine()
678
- : track.raw.engine;
679
- stream = discord_ytdl_core_1.default
680
- .arbitraryStream(arbitrarySource, {
681
- opusEncoded: false,
682
- fmt: "s16le",
683
- encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters_1.default.create(this._activeFilters)] : [],
684
- seek: options.seek ? options.seek / 1000 : 0
685
- })
686
- .on("error", (err) => {
687
- return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err);
688
- });
689
- }
690
- const resource = this.connection.createStream(stream, {
691
- type: voice_1.StreamType.Raw,
692
- data: track,
693
- disableVolume: Boolean(this.options.disableVolume)
694
- });
695
- if (options.seek)
696
- this._streamTime = options.seek;
697
- this._filtersUpdate = options.filtersUpdate;
698
- const volumeTransformer = resource.volume;
699
- if (volumeTransformer && typeof this.options.initialVolume === "number")
700
- Reflect.set(volumeTransformer, "volume", Math.pow(this.options.initialVolume / 100, 1.660964));
701
- if (volumeTransformer?.hasSmoothness && typeof this.options.volumeSmoothness === "number") {
702
- if (typeof volumeTransformer.setSmoothness === "function")
703
- volumeTransformer.setSmoothness(this.options.volumeSmoothness || 0);
704
- }
705
- setTimeout(() => {
706
- this.connection.playStream(resource);
707
- }, tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_getBufferingTimeout).call(this)).unref();
708
- }
709
- /**
710
- * Private method to handle autoplay
711
- * @param {Track} track The source track to find its similar track for autoplay
712
- * @returns {Promise<void>}
713
- * @private
714
- */
715
- async _handleAutoplay(track) {
716
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
717
- return;
718
- if (!track || ![track.source, track.raw?.source].includes("youtube")) {
719
- if (this.options.leaveOnEnd)
720
- this.destroy();
721
- return void this.player.emit("queueEnd", this);
722
- }
723
- const info = await youtube_sr_1.default.getVideo(track.url)
724
- .then((x) => x.videos[0])
725
- .catch(Util_1.Util.noop);
726
- if (!info) {
727
- if (this.options.leaveOnEnd)
728
- this.destroy();
729
- return void this.player.emit("queueEnd", this);
730
- }
731
- const nextTrack = new Track_1.default(this.player, {
732
- title: info.title,
733
- url: `https://www.youtube.com/watch?v=${info.id}`,
734
- duration: info.durationFormatted ? Util_1.Util.buildTimeCode(Util_1.Util.parseMS(info.duration * 1000)) : "0:00",
735
- description: "",
736
- thumbnail: typeof info.thumbnail === "string" ? info.thumbnail : info.thumbnail.url,
737
- views: info.views,
738
- author: info.channel.name,
739
- requestedBy: track.requestedBy,
740
- source: "youtube"
741
- });
742
- this.play(nextTrack, { immediate: true });
743
- }
744
- *[(_Queue_lastVolume = new WeakMap(), _Queue_destroyed = new WeakMap(), _Queue_instances = new WeakSet(), Symbol.iterator)]() {
745
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
746
- return;
747
- yield* this.tracks;
748
- }
749
- /**
750
- * JSON representation of this queue
751
- * @returns {object}
752
- */
753
- toJSON() {
754
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
755
- return;
756
- return {
757
- id: this.id,
758
- guild: this.guild.id,
759
- voiceChannel: this.connection?.channel?.id,
760
- options: this.options,
761
- tracks: this.tracks.map((m) => m.toJSON())
762
- };
763
- }
764
- /**
765
- * String representation of this queue
766
- * @returns {string}
767
- */
768
- toString() {
769
- if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
770
- return;
771
- if (!this.tracks.length)
772
- return "No songs available to display!";
773
- return `**Upcoming Songs:**\n${this.tracks.map((m, i) => `${i + 1}. **${m.title}**`).join("\n")}`;
774
- }
775
- }
776
- exports.Queue = Queue;
777
- _Queue_watchDestroyed = function _Queue_watchDestroyed(emit = true) {
778
- if (tslib_1.__classPrivateFieldGet(this, _Queue_destroyed, "f")) {
779
- if (emit)
780
- this.player.emit("error", this, new PlayerError_1.PlayerError("Cannot use destroyed queue", PlayerError_1.ErrorStatusCode.DESTROYED_QUEUE));
781
- return true;
782
- }
783
- return false;
784
- }, _Queue_getBufferingTimeout = function _Queue_getBufferingTimeout() {
785
- const timeout = this.options.bufferingTimeout;
786
- if (isNaN(timeout) || timeout < 0 || !Number.isFinite(timeout))
787
- return 1000;
788
- return timeout;
789
- };
1
+ "use strict";
2
+ var _Queue_instances, _Queue_lastVolume, _Queue_destroyed, _Queue_watchDestroyed, _Queue_getBufferingTimeout;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Queue = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const discord_js_1 = require("discord.js");
7
+ const Track_1 = tslib_1.__importDefault(require("./Track"));
8
+ const types_1 = require("../types/types");
9
+ const ytdl_core_1 = tslib_1.__importDefault(require("ytdl-core"));
10
+ const voice_1 = require("@discordjs/voice");
11
+ const Util_1 = require("../utils/Util");
12
+ const youtube_sr_1 = tslib_1.__importDefault(require("youtube-sr"));
13
+ const AudioFilters_1 = tslib_1.__importDefault(require("../utils/AudioFilters"));
14
+ const PlayerError_1 = require("./PlayerError");
15
+ const FFmpegStream_1 = require("../utils/FFmpegStream");
16
+ class Queue {
17
+ /**
18
+ * Queue constructor
19
+ * @param {Player} player The player that instantiated this queue
20
+ * @param {Guild} guild The guild that instantiated this queue
21
+ * @param {PlayerOptions} [options] Player options for the queue
22
+ */
23
+ constructor(player, guild, options = {}) {
24
+ _Queue_instances.add(this);
25
+ this.tracks = [];
26
+ this.previousTracks = [];
27
+ this.playing = false;
28
+ this.metadata = null;
29
+ this.repeatMode = 0;
30
+ this.id = discord_js_1.SnowflakeUtil.generate().toString();
31
+ this._streamTime = 0;
32
+ this._cooldownsTimeout = new discord_js_1.Collection();
33
+ this._activeFilters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
34
+ this._filtersUpdate = false;
35
+ _Queue_lastVolume.set(this, 0);
36
+ _Queue_destroyed.set(this, false);
37
+ this.onBeforeCreateStream = null;
38
+ /**
39
+ * The player that instantiated this queue
40
+ * @type {Player}
41
+ * @readonly
42
+ */
43
+ this.player = player;
44
+ /**
45
+ * The guild that instantiated this queue
46
+ * @type {Guild}
47
+ * @readonly
48
+ */
49
+ this.guild = guild;
50
+ /**
51
+ * The player options for this queue
52
+ * @type {PlayerOptions}
53
+ */
54
+ this.options = {};
55
+ /**
56
+ * Queue repeat mode
57
+ * @type {QueueRepeatMode}
58
+ * @name Queue#repeatMode
59
+ */
60
+ /**
61
+ * Queue metadata
62
+ * @type {any}
63
+ * @name Queue#metadata
64
+ */
65
+ /**
66
+ * Previous tracks
67
+ * @type {Track[]}
68
+ * @name Queue#previousTracks
69
+ */
70
+ /**
71
+ * Regular tracks
72
+ * @type {Track[]}
73
+ * @name Queue#tracks
74
+ */
75
+ /**
76
+ * The connection
77
+ * @type {StreamDispatcher}
78
+ * @name Queue#connection
79
+ */
80
+ /**
81
+ * The ID of this queue
82
+ * @type {Snowflake}
83
+ * @name Queue#id
84
+ */
85
+ Object.assign(this.options, {
86
+ leaveOnEnd: true,
87
+ leaveOnStop: true,
88
+ leaveOnEmpty: true,
89
+ leaveOnEmptyCooldown: 1000,
90
+ autoSelfDeaf: true,
91
+ ytdlOptions: {
92
+ highWaterMark: 1 << 25
93
+ },
94
+ initialVolume: 100,
95
+ bufferingTimeout: 3000,
96
+ spotifyBridge: true,
97
+ disableVolume: false
98
+ }, options);
99
+ if ("onBeforeCreateStream" in this.options)
100
+ this.onBeforeCreateStream = this.options.onBeforeCreateStream;
101
+ this.player.emit("debug", this, `Queue initialized:\n\n${this.player.scanDeps()}`);
102
+ }
103
+ /**
104
+ * Returns current track
105
+ * @type {Track}
106
+ */
107
+ get current() {
108
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
109
+ return;
110
+ return this.connection.audioResource?.metadata ?? this.tracks[0];
111
+ }
112
+ /**
113
+ * If this queue is destroyed
114
+ * @type {boolean}
115
+ */
116
+ get destroyed() {
117
+ return tslib_1.__classPrivateFieldGet(this, _Queue_destroyed, "f");
118
+ }
119
+ /**
120
+ * Returns current track
121
+ * @returns {Track}
122
+ */
123
+ nowPlaying() {
124
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
125
+ return;
126
+ return this.current;
127
+ }
128
+ /**
129
+ * Connects to a voice channel
130
+ * @param {GuildChannelResolvable} channel The voice/stage channel
131
+ * @returns {Promise<Queue>}
132
+ */
133
+ async connect(channel) {
134
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
135
+ return;
136
+ const _channel = this.guild.channels.resolve(channel);
137
+ if (![discord_js_1.ChannelType.GuildStageVoice, discord_js_1.ChannelType.GuildVoice].includes(_channel?.type))
138
+ throw new PlayerError_1.PlayerError(`Channel type must be GuildVoice or GuildStageVoice, got ${_channel?.type}!`, PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
139
+ const connection = await this.player.voiceUtils.connect(_channel, {
140
+ deaf: this.options.autoSelfDeaf
141
+ });
142
+ this.connection = connection;
143
+ if (_channel.type === discord_js_1.ChannelType.GuildStageVoice) {
144
+ await _channel.guild.members.me.voice.setSuppressed(false).catch(async () => {
145
+ return await _channel.guild.members.me.voice.setRequestToSpeak(true).catch(Util_1.Util.noop);
146
+ });
147
+ }
148
+ this.connection.on("error", (err) => {
149
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
150
+ return;
151
+ this.player.emit("connectionError", this, err);
152
+ });
153
+ this.connection.on("debug", (msg) => {
154
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
155
+ return;
156
+ this.player.emit("debug", this, msg);
157
+ });
158
+ this.player.emit("connectionCreate", this, this.connection);
159
+ this.connection.on("start", (resource) => {
160
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
161
+ return;
162
+ this.playing = true;
163
+ if (!this._filtersUpdate)
164
+ this.player.emit("trackStart", this, resource?.metadata ?? this.current);
165
+ this._filtersUpdate = false;
166
+ });
167
+ this.connection.on("finish", async (resource) => {
168
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
169
+ return;
170
+ this.playing = false;
171
+ if (this._filtersUpdate)
172
+ return;
173
+ this._streamTime = 0;
174
+ if (resource?.metadata)
175
+ this.previousTracks.push(resource.metadata);
176
+ this.player.emit("trackEnd", this, resource.metadata);
177
+ if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.OFF) {
178
+ if (this.options.leaveOnEnd)
179
+ this.destroy();
180
+ this.player.emit("queueEnd", this);
181
+ }
182
+ else if (!this.tracks.length && this.repeatMode === types_1.QueueRepeatMode.AUTOPLAY) {
183
+ this._handleAutoplay(Util_1.Util.last(this.previousTracks));
184
+ }
185
+ else {
186
+ if (this.repeatMode === types_1.QueueRepeatMode.TRACK)
187
+ return void this.play(Util_1.Util.last(this.previousTracks), { immediate: true });
188
+ if (this.repeatMode === types_1.QueueRepeatMode.QUEUE)
189
+ this.tracks.push(Util_1.Util.last(this.previousTracks));
190
+ const nextTrack = this.tracks.shift();
191
+ this.play(nextTrack, { immediate: true });
192
+ return;
193
+ }
194
+ });
195
+ return this;
196
+ }
197
+ /**
198
+ * Destroys this queue
199
+ * @param {boolean} [disconnect=this.options.leaveOnStop] If it should leave on destroy
200
+ * @returns {void}
201
+ */
202
+ destroy(disconnect = this.options.leaveOnStop) {
203
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
204
+ return;
205
+ if (this.connection)
206
+ this.connection.end();
207
+ if (disconnect)
208
+ this.connection?.disconnect();
209
+ this.player.queues.delete(this.guild.id);
210
+ this.player.voiceUtils.cache.delete(this.guild.id);
211
+ tslib_1.__classPrivateFieldSet(this, _Queue_destroyed, true, "f");
212
+ }
213
+ /**
214
+ * Skips current track
215
+ * @returns {boolean}
216
+ */
217
+ skip() {
218
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
219
+ return;
220
+ if (!this.connection)
221
+ return false;
222
+ this._filtersUpdate = false;
223
+ this.connection.end();
224
+ return true;
225
+ }
226
+ /**
227
+ * Adds single track to the queue
228
+ * @param {Track} track The track to add
229
+ * @returns {void}
230
+ */
231
+ addTrack(track) {
232
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
233
+ return;
234
+ if (!(track instanceof Track_1.default))
235
+ throw new PlayerError_1.PlayerError("invalid track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
236
+ this.tracks.push(track);
237
+ this.player.emit("trackAdd", this, track);
238
+ }
239
+ /**
240
+ * Adds multiple tracks to the queue
241
+ * @param {Track[]} tracks Array of tracks to add
242
+ */
243
+ addTracks(tracks) {
244
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
245
+ return;
246
+ if (!tracks.every((y) => y instanceof Track_1.default))
247
+ throw new PlayerError_1.PlayerError("invalid track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
248
+ this.tracks.push(...tracks);
249
+ this.player.emit("tracksAdd", this, tracks);
250
+ }
251
+ /**
252
+ * Sets paused state
253
+ * @param {boolean} paused The paused state
254
+ * @returns {boolean}
255
+ */
256
+ setPaused(paused) {
257
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
258
+ return;
259
+ if (!this.connection)
260
+ return false;
261
+ return paused ? this.connection.pause(true) : this.connection.resume();
262
+ }
263
+ /**
264
+ * Sets bitrate
265
+ * @param {number|auto} bitrate bitrate to set
266
+ * @returns {void}
267
+ */
268
+ setBitrate(bitrate) {
269
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
270
+ return;
271
+ if (!this.connection?.audioResource?.encoder)
272
+ return;
273
+ if (bitrate === "auto")
274
+ bitrate = this.connection.channel?.bitrate ?? 64000;
275
+ this.connection.audioResource.encoder.setBitrate(bitrate);
276
+ }
277
+ /**
278
+ * Sets volume
279
+ * @param {number} amount The volume amount
280
+ * @returns {boolean}
281
+ */
282
+ setVolume(amount) {
283
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
284
+ return;
285
+ if (!this.connection)
286
+ return false;
287
+ tslib_1.__classPrivateFieldSet(this, _Queue_lastVolume, amount, "f");
288
+ this.options.initialVolume = amount;
289
+ return this.connection.setVolume(amount);
290
+ }
291
+ /**
292
+ * Sets repeat mode
293
+ * @param {QueueRepeatMode} mode The repeat mode
294
+ * @returns {boolean}
295
+ */
296
+ setRepeatMode(mode) {
297
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
298
+ return;
299
+ if (![types_1.QueueRepeatMode.OFF, types_1.QueueRepeatMode.QUEUE, types_1.QueueRepeatMode.TRACK, types_1.QueueRepeatMode.AUTOPLAY].includes(mode))
300
+ throw new PlayerError_1.PlayerError(`Unknown repeat mode "${mode}"!`, PlayerError_1.ErrorStatusCode.UNKNOWN_REPEAT_MODE);
301
+ if (mode === this.repeatMode)
302
+ return false;
303
+ this.repeatMode = mode;
304
+ return true;
305
+ }
306
+ /**
307
+ * The current volume amount
308
+ * @type {number}
309
+ */
310
+ get volume() {
311
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
312
+ return;
313
+ if (!this.connection)
314
+ return 100;
315
+ return this.connection.volume;
316
+ }
317
+ set volume(amount) {
318
+ this.setVolume(amount);
319
+ }
320
+ /**
321
+ * The stream time of this queue
322
+ * @type {number}
323
+ */
324
+ get streamTime() {
325
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
326
+ return;
327
+ if (!this.connection)
328
+ return 0;
329
+ const playbackTime = this._streamTime + this.connection.streamTime;
330
+ const NC = this._activeFilters.includes("nightcore") ? 1.25 : null;
331
+ const VW = this._activeFilters.includes("vaporwave") ? 0.8 : null;
332
+ if (NC && VW)
333
+ return playbackTime * (NC + VW);
334
+ return NC ? playbackTime * NC : VW ? playbackTime * VW : playbackTime;
335
+ }
336
+ set streamTime(time) {
337
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
338
+ return;
339
+ this.seek(time);
340
+ }
341
+ /**
342
+ * Returns enabled filters
343
+ * @returns {AudioFilters}
344
+ */
345
+ getFiltersEnabled() {
346
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
347
+ return;
348
+ return AudioFilters_1.default.names.filter((x) => this._activeFilters.includes(x));
349
+ }
350
+ /**
351
+ * Returns disabled filters
352
+ * @returns {AudioFilters}
353
+ */
354
+ getFiltersDisabled() {
355
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
356
+ return;
357
+ return AudioFilters_1.default.names.filter((x) => !this._activeFilters.includes(x));
358
+ }
359
+ /**
360
+ * Sets filters
361
+ * @param {QueueFilters} filters Queue filters
362
+ * @returns {Promise<void>}
363
+ */
364
+ async setFilters(filters) {
365
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
366
+ return;
367
+ if (!filters || !Object.keys(filters).length) {
368
+ // reset filters
369
+ const streamTime = this.streamTime;
370
+ this._activeFilters = [];
371
+ return await this.play(this.current, {
372
+ immediate: true,
373
+ filtersUpdate: true,
374
+ seek: streamTime,
375
+ encoderArgs: []
376
+ });
377
+ }
378
+ const _filters = []; // eslint-disable-line @typescript-eslint/no-explicit-any
379
+ for (const filter in filters) {
380
+ if (filters[filter] === true)
381
+ _filters.push(filter);
382
+ }
383
+ if (this._activeFilters.join("") === _filters.join(""))
384
+ return;
385
+ const newFilters = AudioFilters_1.default.create(_filters).trim();
386
+ const streamTime = this.streamTime;
387
+ this._activeFilters = _filters;
388
+ return await this.play(this.current, {
389
+ immediate: true,
390
+ filtersUpdate: true,
391
+ seek: streamTime,
392
+ encoderArgs: !_filters.length ? undefined : ["-af", newFilters]
393
+ });
394
+ }
395
+ /**
396
+ * Seeks to the given time
397
+ * @param {number} position The position
398
+ * @returns {boolean}
399
+ */
400
+ async seek(position) {
401
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
402
+ return;
403
+ if (!this.playing || !this.current)
404
+ return false;
405
+ if (position < 1)
406
+ position = 0;
407
+ if (position >= this.current.durationMS)
408
+ return this.skip();
409
+ await this.play(this.current, {
410
+ immediate: true,
411
+ filtersUpdate: true,
412
+ seek: position
413
+ });
414
+ return true;
415
+ }
416
+ /**
417
+ * Plays previous track
418
+ * @returns {Promise<void>}
419
+ */
420
+ async back() {
421
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
422
+ return;
423
+ const prev = this.previousTracks[this.previousTracks.length - 2]; // because last item is the current track
424
+ if (!prev)
425
+ throw new PlayerError_1.PlayerError("Could not find previous track", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
426
+ return await this.play(prev, { immediate: true });
427
+ }
428
+ /**
429
+ * Clear this queue
430
+ */
431
+ clear() {
432
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
433
+ return;
434
+ this.tracks = [];
435
+ this.previousTracks = [];
436
+ }
437
+ /**
438
+ * Stops the player
439
+ * @returns {void}
440
+ */
441
+ stop() {
442
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
443
+ return;
444
+ return this.destroy();
445
+ }
446
+ /**
447
+ * Shuffles this queue
448
+ * @returns {boolean}
449
+ */
450
+ shuffle() {
451
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
452
+ return;
453
+ if (!this.tracks.length || this.tracks.length < 3)
454
+ return false;
455
+ const currentTrack = this.tracks.shift();
456
+ for (let i = this.tracks.length - 1; i > 0; i--) {
457
+ const j = Math.floor(Math.random() * (i + 1));
458
+ [this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
459
+ }
460
+ this.tracks.unshift(currentTrack);
461
+ return true;
462
+ }
463
+ /**
464
+ * Removes a track from the queue
465
+ * @param {Track|string|number} track The track to remove
466
+ * @returns {Track}
467
+ */
468
+ remove(track) {
469
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
470
+ return;
471
+ let trackFound = null;
472
+ if (typeof track === "number") {
473
+ trackFound = this.tracks[track];
474
+ if (trackFound) {
475
+ this.tracks = this.tracks.filter((t) => t.id !== trackFound.id);
476
+ }
477
+ }
478
+ else {
479
+ trackFound = this.tracks.find((s) => s.id === (track instanceof Track_1.default ? track.id : track));
480
+ if (trackFound) {
481
+ this.tracks = this.tracks.filter((s) => s.id !== trackFound.id);
482
+ }
483
+ }
484
+ return trackFound;
485
+ }
486
+ /**
487
+ * Returns the index of the specified track. If found, returns the track index else returns -1.
488
+ * @param {number|Track|string} track The track
489
+ * @returns {number}
490
+ */
491
+ getTrackPosition(track) {
492
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
493
+ return;
494
+ if (typeof track === "number")
495
+ return this.tracks[track] != null ? track : -1;
496
+ return this.tracks.findIndex((pred) => pred.id === (track instanceof Track_1.default ? track.id : track));
497
+ }
498
+ /**
499
+ * Jumps to particular track
500
+ * @param {Track|number} track The track
501
+ * @returns {void}
502
+ */
503
+ jump(track) {
504
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
505
+ return;
506
+ const foundTrack = this.remove(track);
507
+ if (!foundTrack)
508
+ throw new PlayerError_1.PlayerError("Track not found", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
509
+ this.tracks.splice(0, 0, foundTrack);
510
+ return void this.skip();
511
+ }
512
+ /**
513
+ * Jumps to particular track, removing other tracks on the way
514
+ * @param {Track|number} track The track
515
+ * @returns {void}
516
+ */
517
+ skipTo(track) {
518
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
519
+ return;
520
+ const trackIndex = this.getTrackPosition(track);
521
+ const removedTrack = this.remove(track);
522
+ if (!removedTrack)
523
+ throw new PlayerError_1.PlayerError("Track not found", PlayerError_1.ErrorStatusCode.TRACK_NOT_FOUND);
524
+ this.tracks.splice(0, trackIndex, removedTrack);
525
+ return void this.skip();
526
+ }
527
+ /**
528
+ * Inserts the given track to specified index
529
+ * @param {Track} track The track to insert
530
+ * @param {number} [index=0] The index where this track should be
531
+ */
532
+ insert(track, index = 0) {
533
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
534
+ return;
535
+ if (!track || !(track instanceof Track_1.default))
536
+ throw new PlayerError_1.PlayerError("track must be the instance of Track", PlayerError_1.ErrorStatusCode.INVALID_TRACK);
537
+ if (typeof index !== "number" || index < 0 || !Number.isFinite(index))
538
+ throw new PlayerError_1.PlayerError(`Invalid index "${index}"`, PlayerError_1.ErrorStatusCode.INVALID_ARG_TYPE);
539
+ this.tracks.splice(index, 0, track);
540
+ this.player.emit("trackAdd", this, track);
541
+ }
542
+ /**
543
+ * @typedef {object} PlayerTimestamp
544
+ * @property {string} current The current progress
545
+ * @property {string} end The total time
546
+ * @property {number} progress Progress in %
547
+ */
548
+ /**
549
+ * Returns player stream timestamp
550
+ * @returns {PlayerTimestamp}
551
+ */
552
+ getPlayerTimestamp() {
553
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
554
+ return;
555
+ const currentStreamTime = this.streamTime;
556
+ const totalTime = this.current.durationMS;
557
+ const currentTimecode = Util_1.Util.buildTimeCode(Util_1.Util.parseMS(currentStreamTime));
558
+ const endTimecode = Util_1.Util.buildTimeCode(Util_1.Util.parseMS(totalTime));
559
+ return {
560
+ current: currentTimecode,
561
+ end: endTimecode,
562
+ progress: Math.round((currentStreamTime / totalTime) * 100)
563
+ };
564
+ }
565
+ /**
566
+ * Creates progress bar string
567
+ * @param {PlayerProgressbarOptions} options The progress bar options
568
+ * @returns {string}
569
+ */
570
+ createProgressBar(options = { timecodes: true }) {
571
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
572
+ return;
573
+ const length = typeof options.length === "number" ? (options.length <= 0 || options.length === Infinity ? 15 : options.length) : 15;
574
+ const index = Math.round((this.streamTime / this.current.durationMS) * length);
575
+ const indicator = typeof options.indicator === "string" && options.indicator.length > 0 ? options.indicator : "🔘";
576
+ const line = typeof options.line === "string" && options.line.length > 0 ? options.line : "▬";
577
+ if (index >= 1 && index <= length) {
578
+ const bar = line.repeat(length - 1).split("");
579
+ bar.splice(index, 0, indicator);
580
+ if (options.timecodes) {
581
+ const timestamp = this.getPlayerTimestamp();
582
+ return `${timestamp.current} ┃ ${bar.join("")} ┃ ${timestamp.end}`;
583
+ }
584
+ else {
585
+ return `${bar.join("")}`;
586
+ }
587
+ }
588
+ else {
589
+ if (options.timecodes) {
590
+ const timestamp = this.getPlayerTimestamp();
591
+ return `${timestamp.current} ┃ ${indicator}${line.repeat(length - 1)} ┃ ${timestamp.end}`;
592
+ }
593
+ else {
594
+ return `${indicator}${line.repeat(length - 1)}`;
595
+ }
596
+ }
597
+ }
598
+ /**
599
+ * Total duration
600
+ * @type {Number}
601
+ */
602
+ get totalTime() {
603
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
604
+ return;
605
+ return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
606
+ }
607
+ /**
608
+ * Play stream in a voice/stage channel
609
+ * @param {Track} [src] The track to play (if empty, uses first track from the queue)
610
+ * @param {PlayOptions} [options] The options
611
+ * @returns {Promise<void>}
612
+ */
613
+ async play(src, options = {}) {
614
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this, false))
615
+ return;
616
+ if (!this.connection || !this.connection.voiceConnection)
617
+ throw new PlayerError_1.PlayerError("Voice connection is not available, use <Queue>.connect()!", PlayerError_1.ErrorStatusCode.NO_CONNECTION);
618
+ if (src && (this.playing || this.tracks.length) && !options.immediate)
619
+ return this.addTrack(src);
620
+ const track = options.filtersUpdate && !options.immediate ? src || this.current : src ?? this.tracks.shift();
621
+ if (!track)
622
+ return;
623
+ this.player.emit("debug", this, "Received play request");
624
+ if (!options.filtersUpdate) {
625
+ this.previousTracks = this.previousTracks.filter((x) => x.id !== track.id);
626
+ this.previousTracks.push(track);
627
+ }
628
+ let stream = null;
629
+ const hasCustomDownloader = typeof this.onBeforeCreateStream === "function";
630
+ if (["youtube", "spotify"].includes(track.raw.source)) {
631
+ let spotifyResolved = false;
632
+ if (this.options.spotifyBridge && track.raw.source === "spotify" && !track.raw.engine) {
633
+ track.raw.engine = await youtube_sr_1.default.search(`${track.author} ${track.title}`, { type: "video" })
634
+ .then((res) => res[0].url)
635
+ .catch(() => null);
636
+ spotifyResolved = true;
637
+ }
638
+ const url = track.raw.source === "spotify" ? track.raw.engine : track.url;
639
+ if (!url)
640
+ return void this.play(this.tracks.shift(), { immediate: true });
641
+ if (hasCustomDownloader) {
642
+ stream = (await this.onBeforeCreateStream(track, spotifyResolved ? "youtube" : track.raw.source, this)) || null;
643
+ }
644
+ if (!stream) {
645
+ stream = (0, ytdl_core_1.default)(url, this.options.ytdlOptions);
646
+ }
647
+ }
648
+ else {
649
+ const arbitraryStream = (hasCustomDownloader && (await this.onBeforeCreateStream(track, track.raw.source || track.raw.engine, this))) || null;
650
+ stream =
651
+ arbitraryStream || (track.raw.source === "soundcloud" && typeof track.raw.engine?.downloadProgressive === "function")
652
+ ? await track.raw.engine.downloadProgressive()
653
+ : typeof track.raw.engine === "function"
654
+ ? await track.raw.engine()
655
+ : track.raw.engine;
656
+ }
657
+ const ffmpegStream = (0, FFmpegStream_1.createFFmpegStream)(stream, {
658
+ encoderArgs: options.encoderArgs || this._activeFilters.length ? ["-af", AudioFilters_1.default.create(this._activeFilters)] : [],
659
+ seek: options.seek ? options.seek / 1000 : 0,
660
+ fmt: "s16le"
661
+ }).on("error", (err) => {
662
+ if (!`${err}`.toLowerCase().includes("premature close"))
663
+ this.player.emit("error", this, err);
664
+ });
665
+ const resource = this.connection.createStream(ffmpegStream, {
666
+ type: voice_1.StreamType.Raw,
667
+ data: track,
668
+ disableVolume: Boolean(this.options.disableVolume)
669
+ });
670
+ if (options.seek)
671
+ this._streamTime = options.seek;
672
+ this._filtersUpdate = options.filtersUpdate;
673
+ const volumeTransformer = resource.volume;
674
+ if (volumeTransformer && typeof this.options.initialVolume === "number")
675
+ Reflect.set(volumeTransformer, "volume", Math.pow(this.options.initialVolume / 100, 1.660964));
676
+ if (volumeTransformer?.hasSmoothness && typeof this.options.volumeSmoothness === "number") {
677
+ if (typeof volumeTransformer.setSmoothness === "function")
678
+ volumeTransformer.setSmoothness(this.options.volumeSmoothness || 0);
679
+ }
680
+ setTimeout(() => {
681
+ this.connection.playStream(resource);
682
+ }, tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_getBufferingTimeout).call(this)).unref();
683
+ }
684
+ /**
685
+ * Private method to handle autoplay
686
+ * @param {Track} track The source track to find its similar track for autoplay
687
+ * @returns {Promise<void>}
688
+ * @private
689
+ */
690
+ async _handleAutoplay(track) {
691
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
692
+ return;
693
+ if (!track || ![track.source, track.raw?.source].includes("youtube")) {
694
+ if (this.options.leaveOnEnd)
695
+ this.destroy();
696
+ return void this.player.emit("queueEnd", this);
697
+ }
698
+ let info = await youtube_sr_1.default.getVideo(track.url)
699
+ .then((x) => x.videos[0])
700
+ .catch(Util_1.Util.noop);
701
+ // fallback
702
+ if (!info)
703
+ info = await youtube_sr_1.default.search(track.author)
704
+ .then((x) => x[0])
705
+ .catch(Util_1.Util.noop);
706
+ if (!info) {
707
+ if (this.options.leaveOnEnd)
708
+ this.destroy();
709
+ return void this.player.emit("queueEnd", this);
710
+ }
711
+ const nextTrack = new Track_1.default(this.player, {
712
+ title: info.title,
713
+ url: `https://www.youtube.com/watch?v=${info.id}`,
714
+ duration: info.durationFormatted ? Util_1.Util.buildTimeCode(Util_1.Util.parseMS(info.duration * 1000)) : "0:00",
715
+ description: "",
716
+ thumbnail: typeof info.thumbnail === "string" ? info.thumbnail : info.thumbnail.url,
717
+ views: info.views,
718
+ author: info.channel.name,
719
+ requestedBy: track.requestedBy,
720
+ source: "youtube"
721
+ });
722
+ this.play(nextTrack, { immediate: true });
723
+ }
724
+ *[(_Queue_lastVolume = new WeakMap(), _Queue_destroyed = new WeakMap(), _Queue_instances = new WeakSet(), Symbol.iterator)]() {
725
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
726
+ return;
727
+ yield* this.tracks;
728
+ }
729
+ /**
730
+ * JSON representation of this queue
731
+ * @returns {object}
732
+ */
733
+ toJSON() {
734
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
735
+ return;
736
+ return {
737
+ id: this.id,
738
+ guild: this.guild.id,
739
+ voiceChannel: this.connection?.channel?.id,
740
+ options: this.options,
741
+ tracks: this.tracks.map((m) => m.toJSON())
742
+ };
743
+ }
744
+ /**
745
+ * String representation of this queue
746
+ * @returns {string}
747
+ */
748
+ toString() {
749
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_instances, "m", _Queue_watchDestroyed).call(this))
750
+ return;
751
+ if (!this.tracks.length)
752
+ return "No songs available to display!";
753
+ return `**Upcoming Songs:**\n${this.tracks.map((m, i) => `${i + 1}. **${m.title}**`).join("\n")}`;
754
+ }
755
+ }
756
+ exports.Queue = Queue;
757
+ _Queue_watchDestroyed = function _Queue_watchDestroyed(emit = true) {
758
+ if (tslib_1.__classPrivateFieldGet(this, _Queue_destroyed, "f")) {
759
+ if (emit)
760
+ this.player.emit("error", this, new PlayerError_1.PlayerError("Cannot use destroyed queue", PlayerError_1.ErrorStatusCode.DESTROYED_QUEUE));
761
+ return true;
762
+ }
763
+ return false;
764
+ }, _Queue_getBufferingTimeout = function _Queue_getBufferingTimeout() {
765
+ const timeout = this.options.bufferingTimeout;
766
+ if (isNaN(timeout) || timeout < 0 || !Number.isFinite(timeout))
767
+ return 1000;
768
+ return timeout;
769
+ };