magmastream 2.10.5 → 2.10.6-alpha.1

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.
@@ -117,12 +117,6 @@ export declare class Manager extends EventEmitter {
117
117
  * @param {string} guildId - The guild ID of the player to save
118
118
  */
119
119
  savePlayerState(guildId: string): Promise<void>;
120
- /**
121
- * Sleeps for a specified amount of time.
122
- * @param ms The amount of time to sleep in milliseconds.
123
- * @returns A promise that resolves after the specified amount of time.
124
- */
125
- private sleep;
126
120
  private restorePlayerFromState;
127
121
  private restoreQueue;
128
122
  private restorePreviousQueue;
@@ -529,14 +529,6 @@ class Manager extends events_1.EventEmitter {
529
529
  await player.queue.clearPrevious();
530
530
  await player.queue.setCurrent(null);
531
531
  }
532
- /**
533
- * Sleeps for a specified amount of time.
534
- * @param ms The amount of time to sleep in milliseconds.
535
- * @returns A promise that resolves after the specified amount of time.
536
- */
537
- async sleep(ms) {
538
- return new Promise((resolve) => setTimeout(resolve, ms));
539
- }
540
532
  async restorePlayerFromState(node, nodeId, guildId, state, cleanup) {
541
533
  if (!state.guildId || state.node?.options?.identifier !== nodeId)
542
534
  return;
@@ -584,7 +576,6 @@ class Manager extends events_1.EventEmitter {
584
576
  await player.pause(true);
585
577
  await cleanup();
586
578
  this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
587
- await this.sleep(1000);
588
579
  }
589
580
  async restoreQueue(node, player, state, lavaPlayer) {
590
581
  const currentTrack = state.queue.current;
@@ -724,10 +715,9 @@ class Manager extends events_1.EventEmitter {
724
715
  await promises_1.default.mkdir(playersBaseDir, { recursive: true });
725
716
  });
726
717
  const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
727
- for (const file of guildDirs) {
728
- if (!file.isDirectory())
729
- continue;
730
- const guildId = file.name;
718
+ await Promise.all(guildDirs
719
+ .filter((file) => file.isDirectory())
720
+ .map(async ({ name: guildId }) => {
731
721
  const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
732
722
  try {
733
723
  await promises_1.default.access(stateFilePath);
@@ -740,7 +730,7 @@ class Manager extends events_1.EventEmitter {
740
730
  catch (error) {
741
731
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing guild ${guildId}: ${error}`);
742
732
  }
743
- }
733
+ }));
744
734
  }
745
735
  catch (error) {
746
736
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
@@ -750,17 +740,17 @@ class Manager extends events_1.EventEmitter {
750
740
  case Enums_1.StateStorageType.Redis: {
751
741
  try {
752
742
  const keys = await this.redis.keys(`${Utils_1.PlayerUtils.getRedisKey()}playerstore:*`);
753
- for (const key of keys) {
743
+ await Promise.all(keys.map(async (key) => {
754
744
  try {
755
745
  const data = await this.redis.get(key);
756
746
  if (!data)
757
- continue;
747
+ return;
758
748
  const state = JSON.parse(data);
759
749
  if (!state || typeof state !== "object")
760
- continue;
750
+ return;
761
751
  const guildId = key.split(":").pop();
762
752
  if (!guildId)
763
- continue;
753
+ return;
764
754
  await this.restorePlayerFromState(node, nodeId, guildId, state, async () => {
765
755
  await this.redis.del(key);
766
756
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted Redis state: ${key}`);
@@ -769,7 +759,7 @@ class Manager extends events_1.EventEmitter {
769
759
  catch (error) {
770
760
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
771
761
  }
772
- }
762
+ }));
773
763
  }
774
764
  catch (error) {
775
765
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
@@ -11,6 +11,7 @@ const path_1 = tslib_1.__importDefault(require("path"));
11
11
  const Enums_1 = require("./Enums");
12
12
  const MagmastreamError_1 = require("./MagmastreamError");
13
13
  const validSponsorBlocks = Object.values(Enums_1.SponsorBlockSegment).map((segment) => segment.toLowerCase());
14
+ const TRACK_REQUIRED_EVENTS = new Set(["TrackStartEvent", "TrackEndEvent", "TrackStuckEvent", "TrackExceptionEvent"]);
14
15
  class Node {
15
16
  manager;
16
17
  options;
@@ -484,7 +485,9 @@ class Node {
484
485
  this.handlePlayerUpdate(payload);
485
486
  break;
486
487
  case "event":
487
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Node message: ${Utils_1.JSONUtils.safe(payload, 2)}`);
488
+ if (this.manager.listenerCount(Enums_1.ManagerEventTypes.Debug)) {
489
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Node message: ${Utils_1.JSONUtils.safe(payload, 2)}`);
490
+ }
488
491
  await this.handleEvent(payload);
489
492
  break;
490
493
  case "ready":
@@ -567,8 +570,7 @@ class Node {
567
570
  return;
568
571
  const track = await this.resolveEventTrack(player, payload);
569
572
  const type = payload.type;
570
- const trackRequiredEvents = ["TrackStartEvent", "TrackEndEvent", "TrackStuckEvent", "TrackExceptionEvent"];
571
- if (!track && trackRequiredEvents.includes(type)) {
573
+ if (!track && TRACK_REQUIRED_EVENTS.has(type)) {
572
574
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Received ${type} for guild ${payload.guildId}, but neither the payload nor local queue contains a usable track.`);
573
575
  return;
574
576
  }
@@ -1227,7 +1229,9 @@ class Node {
1227
1229
  socketClosed(player, payload) {
1228
1230
  // A move can close the old socket after the new connection is ready.
1229
1231
  this.manager.emit(Enums_1.ManagerEventTypes.SocketClosed, player, payload);
1230
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Websocket closed for player: ${player.guildId} with payload: ${Utils_1.JSONUtils.safe(payload, 2)}`);
1232
+ if (this.manager.listenerCount(Enums_1.ManagerEventTypes.Debug)) {
1233
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Websocket closed for player: ${player.guildId} with payload: ${Utils_1.JSONUtils.safe(payload, 2)}`);
1234
+ }
1231
1235
  }
1232
1236
  /**
1233
1237
  * Emitted when the segments for a track are loaded.
@@ -101,6 +101,7 @@ export declare class Player {
101
101
  * @returns {this} The player instance.
102
102
  */
103
103
  disconnect(): Promise<this>;
104
+ private emitConnectionChange;
104
105
  /**
105
106
  * Destroys the player and clears the queue.
106
107
  * @param {boolean} disconnect - Whether to disconnect the player from the voice channel.
@@ -187,7 +187,7 @@ class Player {
187
187
  // Set the player state to connecting.
188
188
  this.state = Enums_1.StateTypes.Connecting;
189
189
  // Clone the current player state for comparison.
190
- const oldPlayer = this ? { ...this } : null;
190
+ const oldPlayer = { ...this };
191
191
  this.manager.sendPacket({
192
192
  op: 4,
193
193
  d: {
@@ -200,15 +200,7 @@ class Player {
200
200
  // Set the player state to connected.
201
201
  this.state = Enums_1.StateTypes.Connected;
202
202
  // Emit the player state update event.
203
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
204
- changeType: Enums_1.PlayerStateEventTypes.ConnectionChange,
205
- details: {
206
- type: "connection",
207
- action: "connect",
208
- previousConnection: oldPlayer?.state === Enums_1.StateTypes.Connected,
209
- currentConnection: true,
210
- },
211
- });
203
+ this.emitConnectionChange(oldPlayer, "connect");
212
204
  }
213
205
  /**
214
206
  * Disconnects the player from the voice channel.
@@ -218,7 +210,7 @@ class Player {
218
210
  // Set the player state to disconnecting.
219
211
  this.state = Enums_1.StateTypes.Disconnecting;
220
212
  // Clone the current player state for comparison.
221
- const oldPlayer = this ? { ...this } : null;
213
+ const oldPlayer = { ...this };
222
214
  // Pause the player.
223
215
  if (this.options.pauseOnDisconnect) {
224
216
  await this.pause(true);
@@ -238,16 +230,19 @@ class Player {
238
230
  // Set the player state to disconnected.
239
231
  this.state = Enums_1.StateTypes.Disconnected;
240
232
  // Emit the player state update event.
233
+ this.emitConnectionChange(oldPlayer, "disconnect");
234
+ return this;
235
+ }
236
+ emitConnectionChange(oldPlayer, action) {
241
237
  this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
242
238
  changeType: Enums_1.PlayerStateEventTypes.ConnectionChange,
243
239
  details: {
244
240
  type: "connection",
245
- action: "disconnect",
241
+ action,
246
242
  previousConnection: oldPlayer.state === Enums_1.StateTypes.Connected,
247
- currentConnection: false,
243
+ currentConnection: action === "connect",
248
244
  },
249
245
  });
250
- return this;
251
246
  }
252
247
  /**
253
248
  * Destroys the player and clears the queue.
@@ -1340,7 +1335,9 @@ class Player {
1340
1335
  }
1341
1336
  if (!packet?.op)
1342
1337
  return;
1343
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver recieved a payload: ${Utils_1.JSONUtils.safe(textPayload, 2)}`);
1338
+ if (this.manager.listenerCount(Enums_1.ManagerEventTypes.Debug)) {
1339
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver recieved a payload: ${Utils_1.JSONUtils.safe(textPayload, 2)}`);
1340
+ }
1344
1341
  switch (packet.type) {
1345
1342
  case "startSpeakingEvent": {
1346
1343
  this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverStartSpeaking, this, packet.data);
@@ -1355,7 +1352,9 @@ class Player {
1355
1352
  break;
1356
1353
  }
1357
1354
  default: {
1358
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver recieved an unknown payload: ${Utils_1.JSONUtils.safe(textPayload, 2)}`);
1355
+ if (this.manager.listenerCount(Enums_1.ManagerEventTypes.Debug)) {
1356
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver recieved an unknown payload: ${Utils_1.JSONUtils.safe(textPayload, 2)}`);
1357
+ }
1359
1358
  break;
1360
1359
  }
1361
1360
  }
@@ -1408,9 +1407,12 @@ class Player {
1408
1407
  return;
1409
1408
  }
1410
1409
  if (frame.op === 3) {
1411
- const stream = this.voiceReceiverStreams.get(key) ?? { guildId: frame.guildId, userId: frame.userId, type: frame.type, chunks: [] };
1410
+ let stream = this.voiceReceiverStreams.get(key);
1411
+ if (!stream) {
1412
+ stream = { guildId: frame.guildId, userId: frame.userId, type: frame.type, chunks: [] };
1413
+ this.voiceReceiverStreams.set(key, stream);
1414
+ }
1412
1415
  stream.chunks.push(frame.data);
1413
- this.voiceReceiverStreams.set(key, stream);
1414
1416
  this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverData, this, {
1415
1417
  userId: frame.userId,
1416
1418
  guildId: frame.guildId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magmastream",
3
- "version": "2.10.5",
3
+ "version": "2.10.6-alpha.1",
4
4
  "description": "A user-friendly Lavalink client designed for NodeJS.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "prepare": "npm run build",
12
12
  "build": "tsc",
13
13
  "types": "rtb --dist dist",
14
- "test": "npm run build && node --test tests/player-state.test.cjs",
14
+ "test": "npm run build && node --test tests/player-state.test.cjs tests/hot-paths.test.cjs tests/restore-performance.test.cjs",
15
15
  "format": "prettier --write .",
16
16
  "format:check": "prettier --check .",
17
17
  "lint": "eslint \"src/**/*.{ts,js}\"",