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