magmastream 2.9.0-dev.0 → 2.9.0-dev.10

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.
@@ -9,6 +9,7 @@ const Queue_1 = require("./Queue");
9
9
  const Utils_1 = require("./Utils");
10
10
  const _ = tslib_1.__importStar(require("lodash"));
11
11
  const playerCheck_1 = tslib_1.__importDefault(require("../utils/playerCheck"));
12
+ const RedisQueue_1 = require("./RedisQueue");
12
13
  class Player {
13
14
  options;
14
15
  /** The Queue for the Player. */
@@ -50,7 +51,7 @@ class Player {
50
51
  /** The autoplay state of the player. */
51
52
  isAutoplay = false;
52
53
  /** The number of times to try autoplay before emitting queueEnd. */
53
- autoplayTries = null;
54
+ autoplayTries = 3;
54
55
  static _manager;
55
56
  data = {};
56
57
  dynamicLoopInterval = null;
@@ -67,10 +68,6 @@ class Player {
67
68
  this.manager = Utils_1.Structure.get("Player")._manager;
68
69
  if (!this.manager)
69
70
  throw new RangeError("Manager has not been initiated.");
70
- // If a player with the same guild ID already exists, return it.
71
- if (this.manager.players.has(options.guildId)) {
72
- return this.manager.players.get(options.guildId);
73
- }
74
71
  // Check the player options for errors.
75
72
  (0, playerCheck_1.default)(options);
76
73
  // Set the guild ID and voice state.
@@ -91,8 +88,15 @@ class Player {
91
88
  if (!this.node)
92
89
  throw new RangeError("No available nodes.");
93
90
  // Initialize the queue with the guild ID and manager.
94
- this.queue = new Queue_1.Queue(this.guildId, this.manager);
95
- this.queue.previous = new Array();
91
+ if (this.manager.options.stateStorage.type === Manager_1.StateStorageType.Redis) {
92
+ this.queue = new RedisQueue_1.RedisQueue(this.guildId, this.manager);
93
+ }
94
+ else {
95
+ this.queue = new Queue_1.Queue(this.guildId, this.manager);
96
+ }
97
+ if (this.queue instanceof Queue_1.Queue) {
98
+ this.queue.previous = [];
99
+ }
96
100
  // Add the player to the manager's player collection.
97
101
  this.manager.players.set(options.guildId, this);
98
102
  // Set the initial volume.
@@ -225,7 +229,7 @@ class Player {
225
229
  await this.disconnect();
226
230
  }
227
231
  await this.node.rest.destroyPlayer(this.guildId);
228
- this.queue.clear();
232
+ await this.queue.clear();
229
233
  this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, null, {
230
234
  changeType: Manager_1.PlayerStateEventTypes.PlayerDestroy,
231
235
  });
@@ -307,9 +311,9 @@ class Player {
307
311
  }
308
312
  async play(optionsOrTrack, playOptions) {
309
313
  if (typeof optionsOrTrack !== "undefined" && Utils_1.TrackUtils.validate(optionsOrTrack)) {
310
- this.queue.current = optionsOrTrack;
314
+ await this.queue.setCurrent(optionsOrTrack);
311
315
  }
312
- if (!this.queue.current)
316
+ if (!(await this.queue.getCurrent()))
313
317
  throw new RangeError("No current track.");
314
318
  const finalOptions = playOptions
315
319
  ? playOptions
@@ -319,7 +323,7 @@ class Player {
319
323
  await this.node.rest.updatePlayer({
320
324
  guildId: this.guildId,
321
325
  data: {
322
- encodedTrack: this.queue.current?.track,
326
+ encodedTrack: (await this.queue.getCurrent()).track,
323
327
  ...finalOptions,
324
328
  },
325
329
  });
@@ -375,119 +379,9 @@ class Player {
375
379
  * @returns {Promise<Track[]>} - Array of recommended tracks.
376
380
  */
377
381
  async getRecommendedTracks(track) {
378
- const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(this, track);
382
+ const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(track);
379
383
  return tracks;
380
- // const node = this.manager.useableNode;
381
- // if (!node) {
382
- // throw new Error("No available nodes.");
383
- // }
384
- // if (!TrackUtils.validate(track)) {
385
- // throw new RangeError('"Track must be a "Track" or "Track[]');
386
- // }
387
- // // Get the Last.fm API key and the available source managers
388
- // const apiKey = this.manager.options.lastFmApiKey;
389
- // const enabledSources = node.info.sourceManagers;
390
- // // Determine if YouTube should be used
391
- // if (!apiKey && enabledSources.includes("youtube")) {
392
- // // Use YouTube-based autoplay
393
- // return await this.handleYouTubeRecommendations(track);
394
- // }
395
- // if (!apiKey) return [];
396
- // // Handle Last.fm-based autoplay (or other platforms)
397
- // const selectedSource = node.selectPlatform(enabledSources);
398
- // if (selectedSource) {
399
- // // Use the selected source to handle autoplay
400
- // return await this.handlePlatformAutoplay(track, selectedSource, apiKey);
401
- // }
402
- // // If no source is available, return false
403
- // return [];
404
384
  }
405
- // /**
406
- // * Handles YouTube-based recommendations.
407
- // * @param {Track} track - The track to find recommendations for.
408
- // * @returns {Promise<Track[]>} - Array of recommended tracks.
409
- // */
410
- // private async handleYouTubeRecommendations(track: Track): Promise<Track[]> {
411
- // // Check if the previous track has a YouTube URL
412
- // const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
413
- // // Get the video ID from the previous track's URL
414
- // let videoID: string | null = null;
415
- // if (hasYouTubeURL) {
416
- // videoID = track.uri.split("=").pop();
417
- // } else {
418
- // const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: SearchPlatform.YouTube }, track.requester);
419
- // videoID = searchResult.tracks[0]?.uri.split("=").pop();
420
- // }
421
- // // If the video ID is not found, return false
422
- // if (!videoID) return [];
423
- // // Get a random video index between 2 and 24
424
- // let randomIndex: number;
425
- // let searchURI: string;
426
- // do {
427
- // // Generate a random index between 2 and 24
428
- // randomIndex = Math.floor(Math.random() * 23) + 2;
429
- // // Build the search URI
430
- // searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
431
- // } while (track.uri.includes(searchURI));
432
- // // Search for the video and return false if the search fails
433
- // const res = await this.manager.search({ query: searchURI, source: SearchPlatform.YouTube }, track.requester);
434
- // if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return [];
435
- // // Return all track titles that do not have the same URI as the track.uri from before
436
- // return res.tracks.filter((t) => t.uri !== track.uri);
437
- // }
438
- // /**
439
- // * Handles Last.fm-based autoplay (or other platforms).
440
- // * @param {Track} track - The track to find recommendations for.
441
- // * @param {SearchPlatform} source - The selected search platform.
442
- // * @param {string} apiKey - The Last.fm API key.
443
- // * @returns {Promise<Track[]>} - Array of recommended tracks.
444
- // */
445
- // private async handlePlatformAutoplay(track: Track, source: SearchPlatform, apiKey: string): Promise<Track[]> {
446
- // let { author: artist } = track;
447
- // const { title } = track;
448
- // if (!artist || !title) {
449
- // if (!title) {
450
- // // No title provided, search for the artist's top tracks
451
- // const noTitleUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
452
- // const response = await axios.get(noTitleUrl);
453
- // if (response.data.error || !response.data.toptracks?.track?.length) return [];
454
- // const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
455
- // const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: source }, track.requester);
456
- // if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return [];
457
- // const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
458
- // if (!filteredTracks) return [];
459
- // return filteredTracks;
460
- // }
461
- // if (!artist) {
462
- // // No artist provided, search for the track title
463
- // const noArtistUrl = `https://ws.audioscrobbler.com/2.0/?method=track.search&track=${title}&api_key=${apiKey}&format=json`;
464
- // const response = await axios.get(noArtistUrl);
465
- // artist = response.data.results.trackmatches?.track?.[0]?.artist;
466
- // if (!artist) return [];
467
- // }
468
- // }
469
- // // Search for similar tracks to the current track
470
- // const url = `https://ws.audioscrobbler.com/2.0/?method=track.getSimilar&artist=${artist}&track=${title}&limit=10&autocorrect=1&api_key=${apiKey}&format=json`;
471
- // let response: axios.AxiosResponse;
472
- // try {
473
- // response = await axios.get(url);
474
- // } catch (error) {
475
- // if (error) return [];
476
- // }
477
- // if (response.data.error || !response.data.similartracks?.track?.length) {
478
- // // Retry the request if the first attempt fails
479
- // const retryUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
480
- // const retryResponse = await axios.get(retryUrl);
481
- // if (retryResponse.data.error || !retryResponse.data.toptracks?.track?.length) return [];
482
- // const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
483
- // const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: source }, track.requester);
484
- // if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) return [];
485
- // const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
486
- // if (!filteredTracks) return [];
487
- // return filteredTracks;
488
- // }
489
- // return response.data.similartracks.track.filter((t: { uri: string }) => t.uri !== track.uri);
490
- // }
491
385
  /**
492
386
  * Sets the volume of the player.
493
387
  * @param {number} volume - The new volume. Must be between 0 and 1000.
@@ -619,13 +513,13 @@ class Player {
619
513
  * @throws {TypeError} If the repeat parameter is not a boolean.
620
514
  * @throws {RangeError} If the queue size is less than or equal to 1.
621
515
  */
622
- setDynamicRepeat(repeat, ms) {
516
+ async setDynamicRepeat(repeat, ms) {
623
517
  // Validate the repeat parameter
624
518
  if (typeof repeat !== "boolean") {
625
519
  throw new TypeError('Repeat can only be "true" or "false".');
626
520
  }
627
521
  // Ensure the queue has more than one track for dynamic repeat
628
- if (this.queue.size <= 1) {
522
+ if ((await this.queue.size()) <= 1) {
629
523
  throw new RangeError("The queue size must be greater than 1.");
630
524
  }
631
525
  // Clone the current player state for comparison
@@ -636,15 +530,14 @@ class Player {
636
530
  this.queueRepeat = false;
637
531
  this.dynamicRepeat = true;
638
532
  // Set an interval to shuffle the queue periodically
639
- this.dynamicLoopInterval = setInterval(() => {
533
+ this.dynamicLoopInterval = setInterval(async () => {
640
534
  if (!this.dynamicRepeat)
641
535
  return;
642
536
  // Shuffle the queue and replace it with the shuffled tracks
643
- const shuffled = _.shuffle(this.queue);
644
- this.queue.clear();
645
- shuffled.forEach((track) => {
646
- this.queue.add(track);
647
- });
537
+ const tracks = await this.queue.getTracks();
538
+ const shuffled = _.shuffle(tracks);
539
+ await this.queue.clear();
540
+ await this.queue.add(shuffled);
648
541
  }, ms);
649
542
  // Store the ms value
650
543
  this.dynamicRepeatIntervalMs = ms;
@@ -675,9 +568,9 @@ class Player {
675
568
  */
676
569
  async restart() {
677
570
  // Check if there is a current track in the queue
678
- if (!this.queue.current?.track) {
571
+ if (!(await this.queue.getCurrent())?.track) {
679
572
  // If the queue has tracks, play the next one
680
- if (this.queue.length)
573
+ if (await this.queue.size())
681
574
  await this.play();
682
575
  return this;
683
576
  }
@@ -686,7 +579,7 @@ class Player {
686
579
  guildId: this.guildId,
687
580
  data: {
688
581
  position: 0,
689
- encodedTrack: this.queue.current?.track,
582
+ encodedTrack: (await this.queue.getCurrent())?.track,
690
583
  },
691
584
  });
692
585
  return this;
@@ -701,10 +594,10 @@ class Player {
701
594
  const oldPlayer = { ...this };
702
595
  let removedTracks = [];
703
596
  if (typeof amount === "number" && amount > 1) {
704
- if (amount > this.queue.length)
597
+ if (amount > (await this.queue.size()))
705
598
  throw new RangeError("Cannot skip more than the queue length.");
706
- removedTracks = this.queue.slice(0, amount - 1);
707
- this.queue.splice(0, amount - 1);
599
+ removedTracks = await this.queue.getSlice(0, amount - 1);
600
+ await this.queue.modifyAt(0, amount - 1);
708
601
  }
709
602
  this.node.rest.updatePlayer({
710
603
  guildId: this.guildId,
@@ -764,7 +657,7 @@ class Player {
764
657
  */
765
658
  async previous() {
766
659
  // Check if there are previous tracks in the queue.
767
- if (!this.queue.previous.length) {
660
+ if (!(await this.queue.getPrevious()).length) {
768
661
  throw new Error("No previous track available.");
769
662
  }
770
663
  // Capture the current state of the player before making changes.
@@ -772,7 +665,7 @@ class Player {
772
665
  // Store the current track before changing it.
773
666
  // let currentTrackBeforeChange: Track | null = this.queue.current ? (this.queue.current as Track) : null;
774
667
  // Get the last played track and remove it from the history
775
- const lastTrack = this.queue.previous.pop();
668
+ const lastTrack = (await this.queue.getPrevious()).pop();
776
669
  // Set the skip flag to true to prevent the onTrackEnd event from playing the next track.
777
670
  this.set("skipFlag", true);
778
671
  await this.play(lastTrack);
@@ -798,7 +691,7 @@ class Player {
798
691
  * @emits {PlayerStateUpdate} - With {@link PlayerStateEventTypes.TrackChange} as the change type.
799
692
  */
800
693
  async seek(position) {
801
- if (!this.queue.current)
694
+ if (!(await this.queue.getCurrent()))
802
695
  return undefined;
803
696
  position = Number(position);
804
697
  // Check if the position is valid.
@@ -808,8 +701,8 @@ class Player {
808
701
  // Get the old player state.
809
702
  const oldPlayer = this ? { ...this } : null;
810
703
  // Clamp the position to ensure it is within the valid range.
811
- if (position < 0 || position > this.queue.current.duration) {
812
- position = Math.max(Math.min(position, this.queue.current.duration), 0);
704
+ if (position < 0 || position > (await this.queue.getCurrent()).duration) {
705
+ position = Math.max(Math.min(position, (await this.queue.getCurrent()).duration), 0);
813
706
  }
814
707
  // Update the player's position.
815
708
  this.position = position;
@@ -874,7 +767,7 @@ class Player {
874
767
  try {
875
768
  const playerPosition = this.position;
876
769
  const { sessionId, event: { token, endpoint }, } = this.voiceState;
877
- const currentTrack = this.queue.current ? this.queue.current : null;
770
+ const currentTrack = (await this.queue.getCurrent()) ? await this.queue.getCurrent() : null;
878
771
  await this.node.rest.destroyPlayer(this.guildId).catch(() => { });
879
772
  this.manager.players.delete(this.guildId);
880
773
  this.node = node;
@@ -905,7 +798,7 @@ class Player {
905
798
  if (!newOptions.textChannelId)
906
799
  throw new Error("Text channel ID is required");
907
800
  // Check if a player already exists for the new guild
908
- let newPlayer = this.manager.players.get(newOptions.guildId);
801
+ let newPlayer = await this.manager.getPlayer(newOptions.guildId);
909
802
  // If the player already exists and force is false, return the existing player
910
803
  if (newPlayer && !force)
911
804
  return newPlayer;
@@ -916,9 +809,9 @@ class Player {
916
809
  volume: this.volume,
917
810
  position: this.position,
918
811
  queue: {
919
- current: this.queue.current,
920
- tracks: [...this.queue],
921
- previous: [...this.queue.previous],
812
+ current: await this.queue.getCurrent(),
813
+ tracks: [...(await this.queue.getTracks())],
814
+ previous: [...(await this.queue.getPrevious())],
922
815
  },
923
816
  trackRepeat: this.trackRepeat,
924
817
  queueRepeat: this.queueRepeat,
@@ -938,7 +831,7 @@ class Player {
938
831
  newOptions.selfMute = newOptions.selfMute ?? oldPlayerProperties.selfMute;
939
832
  newOptions.volume = newOptions.volume ?? oldPlayerProperties.volume;
940
833
  // Deep clone the current player
941
- const clonedPlayer = this.manager.create(newOptions);
834
+ const clonedPlayer = await this.manager.create(newOptions);
942
835
  // Connect the cloned player to the new voice channel
943
836
  clonedPlayer.connect();
944
837
  // Update the player's state on the Lavalink node
@@ -951,9 +844,9 @@ class Player {
951
844
  encodedTrack: oldPlayerProperties.queue.current?.track,
952
845
  },
953
846
  });
954
- clonedPlayer.queue.current = oldPlayerProperties.queue.current;
955
- clonedPlayer.queue.previous = oldPlayerProperties.queue.previous;
956
- clonedPlayer.queue.add(oldPlayerProperties.queue.tracks);
847
+ await clonedPlayer.queue.setCurrent(oldPlayerProperties.queue.current);
848
+ await clonedPlayer.queue.addPrevious(oldPlayerProperties.queue.previous);
849
+ await clonedPlayer.queue.add(oldPlayerProperties.queue.tracks);
957
850
  clonedPlayer.filters = oldPlayerProperties.filters;
958
851
  clonedPlayer.isAutoplay = oldPlayerProperties.isAutoplay;
959
852
  clonedPlayer.nowPlayingMessage = oldPlayerProperties.nowPlayingMessage;
@@ -968,7 +861,7 @@ class Player {
968
861
  // Debug information
969
862
  const debugInfo = {
970
863
  success: true,
971
- message: `Transferred ${clonedPlayer.queue.length} tracks successfully to <#${newOptions.voiceChannelId}> bound to <#${newOptions.textChannelId}>.`,
864
+ message: `Transferred ${await clonedPlayer.queue.size()} tracks successfully to <#${newOptions.voiceChannelId}> bound to <#${newOptions.textChannelId}>.`,
972
865
  player: {
973
866
  guildId: clonedPlayer.guildId,
974
867
  voiceChannelId: clonedPlayer.voiceChannelId,
@@ -995,7 +888,7 @@ class Player {
995
888
  throw new RangeError(`There is no lavalyrics-plugin available in the Lavalink node: ${this.node.options.identifier}`);
996
889
  }
997
890
  // Fetch the lyrics for the current track from the Lavalink node
998
- let result = (await this.node.getLyrics(this.queue.current, skipTrackSource));
891
+ let result = (await this.node.getLyrics(await this.queue.getCurrent(), skipTrackSource));
999
892
  // If no lyrics are found, return a default empty lyrics object
1000
893
  if (!result) {
1001
894
  result = {
@@ -6,14 +6,52 @@ const Manager_1 = require("./Manager"); // Import Manager to access emit method
6
6
  * The player's queue, the `current` property is the currently playing track, think of the rest as the up-coming tracks.
7
7
  */
8
8
  class Queue extends Array {
9
+ /** The current track */
10
+ current = null;
11
+ /** The previous tracks */
12
+ previous = [];
13
+ /** The Manager instance. */
14
+ manager;
15
+ /** The guild ID property. */
16
+ guildId;
17
+ /**
18
+ * Constructs a new Queue.
19
+ * @param guildId The guild ID.
20
+ * @param manager The Manager instance.
21
+ */
22
+ constructor(guildId, manager) {
23
+ super();
24
+ /** The Manager instance. */
25
+ this.manager = manager;
26
+ /** The guild property. */
27
+ this.guildId = guildId;
28
+ }
29
+ async getCurrent() {
30
+ return this.current;
31
+ }
32
+ async setCurrent(track) {
33
+ this.current = track;
34
+ }
35
+ async getPrevious() {
36
+ return this.previous;
37
+ }
38
+ async addPrevious(track) {
39
+ if (Array.isArray(track)) {
40
+ this.previous.unshift(...track);
41
+ }
42
+ else {
43
+ this.previous.unshift(track);
44
+ }
45
+ }
46
+ async clearPrevious() {
47
+ this.previous = [];
48
+ }
9
49
  /**
10
50
  * The total duration of the queue in milliseconds.
11
51
  * This includes the duration of the currently playing track.
12
52
  */
13
- get duration() {
14
- // Get the duration of the currently playing track, or 0 if there is none.
53
+ async duration() {
15
54
  const current = this.current?.duration ?? 0;
16
- // Return the sum of all durations in the queue including the current track.
17
55
  return this.reduce((acc, cur) => acc + (cur.duration || 0), current);
18
56
  }
19
57
  /**
@@ -21,7 +59,7 @@ class Queue extends Array {
21
59
  * This includes the current track if it is not null.
22
60
  * @returns The total size of tracks in the queue including the current track.
23
61
  */
24
- get totalSize() {
62
+ async totalSize() {
25
63
  return this.length + (this.current ? 1 : 0);
26
64
  }
27
65
  /**
@@ -29,40 +67,20 @@ class Queue extends Array {
29
67
  * This does not include the currently playing track.
30
68
  * @returns The size of tracks in the queue.
31
69
  */
32
- get size() {
70
+ async size() {
33
71
  return this.length;
34
72
  }
35
- /** The current track */
36
- current = null;
37
- /** The previous tracks */
38
- previous = [];
39
- /** The Manager instance. */
40
- manager;
41
- /** The guild ID property. */
42
- guildId;
43
- /**
44
- * Constructs a new Queue.
45
- * @param guildId The guild ID.
46
- * @param manager The Manager instance.
47
- */
48
- constructor(guildId, manager) {
49
- super();
50
- /** The Manager instance. */
51
- this.manager = manager;
52
- /** The guild property. */
53
- this.guildId = guildId;
54
- }
55
73
  /**
56
74
  * Adds a track to the queue.
57
75
  * @param track The track or tracks to add. Can be a single `Track` or an array of `Track`s.
58
76
  * @param [offset=null] The position to add the track(s) at. If not provided, the track(s) will be added at the end of the queue.
59
77
  */
60
- add(track, offset) {
78
+ async add(track, offset) {
61
79
  // Get the track info as a string
62
80
  const trackInfo = Array.isArray(track) ? track.map((t) => JSON.stringify(t, null, 2)).join(", ") : JSON.stringify(track, null, 2);
63
81
  // Emit a debug message
64
82
  this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[QUEUE] Added ${Array.isArray(track) ? track.length : 1} track(s) to queue: ${trackInfo}`);
65
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
83
+ const oldPlayer = (await this.manager.players.get(this.guildId)) ? { ...(await this.manager.players.get(this.guildId)) } : null;
66
84
  // If the track is valid, add it to the queue
67
85
  // If the queue is empty, set the track as the current track
68
86
  if (!this.current) {
@@ -103,11 +121,11 @@ class Queue extends Array {
103
121
  }
104
122
  }
105
123
  }
106
- if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
124
+ if ((await this.manager.players.has(this.guildId)) && (await this.manager.players.get(this.guildId)).isAutoplay) {
107
125
  if (!Array.isArray(track)) {
108
- const botUser = this.manager.players.get(this.guildId).get("Internal_BotUser");
126
+ const botUser = (await (await this.manager.players.get(this.guildId)).get("Internal_BotUser"));
109
127
  if (botUser && botUser.id === track.requester.id) {
110
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
128
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
111
129
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
112
130
  details: {
113
131
  changeType: "autoPlayAdd",
@@ -119,7 +137,7 @@ class Queue extends Array {
119
137
  }
120
138
  }
121
139
  // Emit a player state update event with the added track(s)
122
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
140
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
123
141
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
124
142
  details: {
125
143
  changeType: "add",
@@ -127,8 +145,8 @@ class Queue extends Array {
127
145
  },
128
146
  });
129
147
  }
130
- remove(startOrPosition = 0, end) {
131
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
148
+ async remove(startOrPosition = 0, end) {
149
+ const oldPlayer = (await this.manager.players.get(this.guildId)) ? { ...(await this.manager.players.get(this.guildId)) } : null;
132
150
  if (typeof end !== "undefined") {
133
151
  // Validate input for `start` and `end`
134
152
  if (isNaN(Number(startOrPosition)) || isNaN(Number(end))) {
@@ -139,7 +157,7 @@ class Queue extends Array {
139
157
  }
140
158
  const removedTracks = this.splice(startOrPosition, end - startOrPosition);
141
159
  this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[QUEUE] Removed ${removedTracks.length} track(s) from player: ${this.guildId} from position ${startOrPosition} to ${end}.`);
142
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
160
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
143
161
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
144
162
  details: {
145
163
  changeType: "remove",
@@ -153,7 +171,7 @@ class Queue extends Array {
153
171
  this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[QUEUE] Removed 1 track from player: ${this.guildId} from position ${startOrPosition}: ${JSON.stringify(removedTrack[0], null, 2)}`);
154
172
  // Ensure removedTrack is an array for consistency
155
173
  const tracksToEmit = removedTrack.length > 0 ? removedTrack : [];
156
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
174
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
157
175
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
158
176
  details: {
159
177
  changeType: "remove",
@@ -166,13 +184,13 @@ class Queue extends Array {
166
184
  * Clears the queue.
167
185
  * This will remove all tracks from the queue and emit a state update event.
168
186
  */
169
- clear() {
187
+ async clear() {
170
188
  // Capture the current state of the player for event emission.
171
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
189
+ const oldPlayer = (await this.manager.players.get(this.guildId)) ? { ...(await this.manager.players.get(this.guildId)) } : null;
172
190
  // Remove all items from the queue.
173
191
  this.splice(0);
174
192
  // Emit an event to update the player state indicating the queue has been cleared.
175
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
193
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
176
194
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
177
195
  details: {
178
196
  changeType: "clear",
@@ -186,16 +204,16 @@ class Queue extends Array {
186
204
  * Shuffles the queue.
187
205
  * This will randomize the order of the tracks in the queue and emit a state update event.
188
206
  */
189
- shuffle() {
207
+ async shuffle() {
190
208
  // Capture the current state of the player for event emission.
191
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
209
+ const oldPlayer = (await this.manager.players.get(this.guildId)) ? { ...(await this.manager.players.get(this.guildId)) } : null;
192
210
  // Shuffle the queue.
193
211
  for (let i = this.length - 1; i > 0; i--) {
194
212
  const j = Math.floor(Math.random() * (i + 1));
195
213
  [this[i], this[j]] = [this[j], this[i]];
196
214
  }
197
215
  // Emit an event to update the player state indicating the queue has been shuffled.
198
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
216
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
199
217
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
200
218
  details: {
201
219
  changeType: "shuffle",
@@ -207,9 +225,9 @@ class Queue extends Array {
207
225
  /**
208
226
  * Shuffles the queue to play tracks requested by each user one block at a time.
209
227
  */
210
- userBlockShuffle() {
228
+ async userBlockShuffle() {
211
229
  // Capture the current state of the player for event emission.
212
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
230
+ const oldPlayer = (await this.manager.players.get(this.guildId)) ? { ...(await this.manager.players.get(this.guildId)) } : null;
213
231
  // Group the tracks in the queue by the user that requested them.
214
232
  const userTracks = new Map();
215
233
  this.forEach((track) => {
@@ -235,7 +253,7 @@ class Queue extends Array {
235
253
  this.splice(0);
236
254
  this.add(shuffledQueue);
237
255
  // Emit an event to update the player state indicating the queue has been shuffled.
238
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
256
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
239
257
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
240
258
  details: {
241
259
  changeType: "userBlock",
@@ -247,8 +265,8 @@ class Queue extends Array {
247
265
  /**
248
266
  * Shuffles the queue to play tracks requested by each user one by one.
249
267
  */
250
- roundRobinShuffle() {
251
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
268
+ async roundRobinShuffle() {
269
+ const oldPlayer = (await this.manager.players.get(this.guildId)) ? { ...(await this.manager.players.get(this.guildId)) } : null;
252
270
  const userTracks = new Map();
253
271
  // Group the tracks in the queue by the user that requested them.
254
272
  this.forEach((track) => {
@@ -283,7 +301,7 @@ class Queue extends Array {
283
301
  this.splice(0);
284
302
  this.add(shuffledQueue);
285
303
  // Emit an event to update the player state indicating the queue has been shuffled.
286
- this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
304
+ this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, await this.manager.players.get(this.guildId), {
287
305
  changeType: Manager_1.PlayerStateEventTypes.QueueChange,
288
306
  details: {
289
307
  changeType: "roundRobin",
@@ -292,5 +310,40 @@ class Queue extends Array {
292
310
  // Emit a debug message indicating the queue has been shuffled for a specific guild ID.
293
311
  this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[QUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
294
312
  }
313
+ async dequeue() {
314
+ return super.shift();
315
+ }
316
+ async enqueueFront(track) {
317
+ if (Array.isArray(track)) {
318
+ this.unshift(...track);
319
+ }
320
+ else {
321
+ this.unshift(track);
322
+ }
323
+ }
324
+ async getTracks() {
325
+ return [...this]; // clone to avoid direct mutation
326
+ }
327
+ async getSlice(start, end) {
328
+ return this.slice(start, end); // Native sync method, still wrapped in a Promise
329
+ }
330
+ async modifyAt(start, deleteCount = 0, ...items) {
331
+ return super.splice(start, deleteCount, ...items);
332
+ }
333
+ async mapAsync(callback) {
334
+ return this.map(callback);
335
+ }
336
+ async filterAsync(callback) {
337
+ return this.filter(callback);
338
+ }
339
+ async findAsync(callback) {
340
+ return this.find(callback);
341
+ }
342
+ async someAsync(callback) {
343
+ return this.some(callback);
344
+ }
345
+ async everyAsync(callback) {
346
+ return this.every(callback);
347
+ }
295
348
  }
296
349
  exports.Queue = Queue;