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