magmastream 2.9.0-dev.40 → 2.9.0-dev.42

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.
package/dist/index.d.ts CHANGED
@@ -965,10 +965,10 @@ interface NodeOptions {
965
965
  * @default false
966
966
  */
967
967
  enableSessionResumeOption?: boolean;
968
- /** The time the lavalink server will wait before it removes the player.
969
- * @default 1000
968
+ /** The time in seconds the lavalink server will wait before it removes the player.
969
+ * @default 60
970
970
  */
971
- sessionTimeoutMs?: number;
971
+ sessionTimeoutSeconds?: number;
972
972
  /** The timeout used for api calls.
973
973
  * @default 10000
974
974
  */
@@ -981,6 +981,10 @@ interface NodeOptions {
981
981
  * @default false
982
982
  */
983
983
  isNodeLink?: boolean;
984
+ /** Whether the node is a backup node.
985
+ * @default false
986
+ */
987
+ isBackup?: boolean;
984
988
  }
985
989
  /**
986
990
  * Discord Packet
@@ -1811,8 +1815,8 @@ interface PlayerOptions {
1811
1815
  textChannelId: string;
1812
1816
  /** The voice channel the Player belongs to. */
1813
1817
  voiceChannelId?: string;
1814
- /** The node the Player uses. */
1815
- node?: string;
1818
+ /** The node identifier the Player uses. */
1819
+ nodeIdentifier?: string;
1816
1820
  /** The initial volume the Player will use. */
1817
1821
  volume?: number;
1818
1822
  /** If the player should mute itself. */
@@ -70,7 +70,7 @@ class Manager extends events_1.EventEmitter {
70
70
  password: "Try BlackForHosting",
71
71
  useSSL: true,
72
72
  enableSessionResumeOption: false,
73
- sessionTimeoutMs: 1000,
73
+ sessionTimeoutSeconds: 1000,
74
74
  nodePriority: 69,
75
75
  },
76
76
  ],
@@ -498,7 +498,7 @@ class Manager extends events_1.EventEmitter {
498
498
  voiceChannelId: state.options.voiceChannelId,
499
499
  selfDeafen: state.options.selfDeafen,
500
500
  volume: lavaPlayer.volume || state.options.volume,
501
- node: nodeId,
501
+ nodeIdentifier: nodeId,
502
502
  };
503
503
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
504
504
  const player = this.create(playerOptions);
@@ -696,7 +696,7 @@ class Manager extends events_1.EventEmitter {
696
696
  voiceChannelId: state.options.voiceChannelId,
697
697
  selfDeafen: state.options.selfDeafen,
698
698
  volume: lavaPlayer?.volume || state.options.volume,
699
- node: nodeId,
699
+ nodeIdentifier: nodeId,
700
700
  };
701
701
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
702
702
  const player = this.create(playerOptions);
@@ -1127,30 +1127,30 @@ class Manager extends events_1.EventEmitter {
1127
1127
  switch (this.options.stateStorage.type) {
1128
1128
  case Enums_1.StateStorageType.JSON:
1129
1129
  {
1130
- const playersStoreDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playersStore");
1131
- const playersDataDir = this.options.stateStorage?.jsonConfig?.path ?? path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
1130
+ const playerStoreDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
1131
+ const playerDataDir = this.options.stateStorage?.jsonConfig?.path ?? path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
1132
1132
  try {
1133
- await promises_1.default.mkdir(playersStoreDir, { recursive: true });
1134
- await promises_1.default.mkdir(playersDataDir, { recursive: true });
1133
+ await promises_1.default.mkdir(playerStoreDir, { recursive: true });
1134
+ await promises_1.default.mkdir(playerDataDir, { recursive: true });
1135
1135
  const activeGuildIds = new Set(this.players.keys());
1136
- // Clean up playersStore/*.json
1137
- const playerStateFiles = await promises_1.default.readdir(playersStoreDir);
1136
+ // Clean up playerStore/*.json
1137
+ const playerStateFiles = await promises_1.default.readdir(playerStoreDir);
1138
1138
  for (const file of playerStateFiles) {
1139
1139
  const guildId = path_1.default.basename(file, ".json");
1140
1140
  if (!activeGuildIds.has(guildId)) {
1141
- const filePath = path_1.default.join(playersStoreDir, file);
1141
+ const filePath = path_1.default.join(playerStoreDir, file);
1142
1142
  await promises_1.default.unlink(filePath);
1143
1143
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player state: ${guildId}`);
1144
1144
  }
1145
1145
  }
1146
1146
  // Clean up players/<guildId>/ folders
1147
- const guildDirs = await promises_1.default.readdir(playersDataDir, { withFileTypes: true });
1147
+ const guildDirs = await promises_1.default.readdir(playerDataDir, { withFileTypes: true });
1148
1148
  for (const dirent of guildDirs) {
1149
1149
  if (!dirent.isDirectory())
1150
1150
  continue;
1151
1151
  const guildId = dirent.name;
1152
1152
  if (!activeGuildIds.has(guildId)) {
1153
- const guildPath = path_1.default.join(playersDataDir, guildId);
1153
+ const guildPath = path_1.default.join(playerDataDir, guildId);
1154
1154
  await promises_1.default.rm(guildPath, { recursive: true, force: true });
1155
1155
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
1156
1156
  }
@@ -1202,7 +1202,7 @@ class Manager extends events_1.EventEmitter {
1202
1202
  switch (this.options.stateStorage.type) {
1203
1203
  case Enums_1.StateStorageType.JSON:
1204
1204
  {
1205
- const playersStoreDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playersStore");
1205
+ const playersStoreDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
1206
1206
  const playersDataDir = this.options.stateStorage?.jsonConfig?.path ?? path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
1207
1207
  try {
1208
1208
  if (!this.players.has(guildId)) {
@@ -1344,7 +1344,7 @@ class Manager extends events_1.EventEmitter {
1344
1344
  */
1345
1345
  get leastLoadNode() {
1346
1346
  return this.nodes
1347
- .filter((node) => node.connected)
1347
+ .filter((node) => node.connected && !node.options.isBackup)
1348
1348
  .sort((a, b) => {
1349
1349
  const aload = a.stats.cpu ? (a.stats.cpu.lavalinkLoad / a.stats.cpu.cores) * 100 : 0;
1350
1350
  const bload = b.stats.cpu ? (b.stats.cpu.lavalinkLoad / b.stats.cpu.cores) * 100 : 0;
@@ -1359,9 +1359,7 @@ class Manager extends events_1.EventEmitter {
1359
1359
  * @returns {Collection<string, Node>} A collection of nodes sorted by player count.
1360
1360
  */
1361
1361
  get leastPlayersNode() {
1362
- return this.nodes
1363
- .filter((node) => node.connected) // Filter out nodes that are not connected
1364
- .sort((a, b) => a.stats.players - b.stats.players); // Sort by the number of players
1362
+ return this.nodes.filter((node) => node.connected && !node.options.isBackup).sort((a, b) => a.stats.players - b.stats.players);
1365
1363
  }
1366
1364
  /**
1367
1365
  * Returns a node based on priority.
@@ -56,10 +56,11 @@ class Node {
56
56
  maxRetryAttempts: options.maxRetryAttempts ?? 30,
57
57
  retryDelayMs: options.retryDelayMs ?? 60000,
58
58
  enableSessionResumeOption: options.enableSessionResumeOption ?? false,
59
- sessionTimeoutMs: options.sessionTimeoutMs ?? 1000,
59
+ sessionTimeoutSeconds: options.sessionTimeoutSeconds ?? 60,
60
60
  apiRequestTimeoutMs: options.apiRequestTimeoutMs ?? 10000,
61
61
  nodePriority: options.nodePriority ?? 0,
62
62
  isNodeLink: options.isNodeLink ?? false,
63
+ isBackup: options.isBackup ?? false,
63
64
  };
64
65
  if (this.options.useSSL) {
65
66
  this.options.port = 443;
@@ -373,6 +374,10 @@ class Node {
373
374
  };
374
375
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Connected node: ${JSON.stringify(debugInfo)}`);
375
376
  this.manager.emit(Enums_1.ManagerEventTypes.NodeConnect, this);
377
+ const playersOnBackupNode = this.manager.players.filter((p) => p.node.options.isBackup);
378
+ if (playersOnBackupNode.size) {
379
+ Promise.all(Array.from(playersOnBackupNode.values(), (player) => player.moveNode(this.options.identifier)));
380
+ }
376
381
  }
377
382
  /**
378
383
  * Handles the "close" event emitted by the WebSocket connection.
@@ -401,6 +406,13 @@ class Node {
401
406
  await Promise.all(Array.from(players.values(), (player) => player.autoMoveNode()));
402
407
  }
403
408
  }
409
+ else {
410
+ const backUpNodes = this.manager.nodes.filter((node) => node.options.isBackup && node.connected);
411
+ const backupNode = backUpNodes.first();
412
+ if (backupNode) {
413
+ await Promise.all(Array.from(this.manager.players.values(), (player) => player.moveNode(backupNode.options.identifier)));
414
+ }
415
+ }
404
416
  if (code !== 1000 || reason !== "destroy")
405
417
  await this.reconnect();
406
418
  }
@@ -474,7 +486,7 @@ class Node {
474
486
  if (this.options.enableSessionResumeOption) {
475
487
  await this.rest.patch(`/v4/sessions/${this.sessionId}`, {
476
488
  resuming: this.options.enableSessionResumeOption,
477
- timeout: this.options.sessionTimeoutMs,
489
+ timeout: this.options.sessionTimeoutSeconds,
478
490
  });
479
491
  }
480
492
  break;
@@ -92,7 +92,7 @@ class Player {
92
92
  if (options.textChannelId)
93
93
  this.textChannelId = options.textChannelId;
94
94
  // Set the node to use, either the specified node or the first available node.
95
- const node = this.manager.nodes.get(options.node);
95
+ const node = this.manager.nodes.get(options.nodeIdentifier);
96
96
  this.node = node || this.manager.useableNode;
97
97
  // If no node is available, throw an error.
98
98
  if (!this.node)
@@ -862,7 +862,7 @@ class Player {
862
862
  if (force && newPlayer) {
863
863
  await newPlayer.destroy();
864
864
  }
865
- newOptions.node = newOptions.node ?? this.options.node;
865
+ newOptions.nodeIdentifier = newOptions.nodeIdentifier ?? this.options.nodeIdentifier;
866
866
  newOptions.selfDeafen = newOptions.selfDeafen ?? oldPlayerProperties.selfDeafen;
867
867
  newOptions.selfMute = newOptions.selfMute ?? oldPlayerProperties.selfMute;
868
868
  newOptions.volume = newOptions.volume ?? oldPlayerProperties.volume;
@@ -12,7 +12,7 @@ function nodeCheck(options) {
12
12
  throw new TypeError("NodeOptions must not be empty.");
13
13
  // Validate the host option
14
14
  // The host option must be present and be a non-empty string.
15
- const { host, identifier, password, port, enableSessionResumeOption, sessionTimeoutMs, maxRetryAttempts, retryDelayMs, useSSL, nodePriority } = options;
15
+ const { host, identifier, password, port, enableSessionResumeOption, sessionTimeoutSeconds, maxRetryAttempts, retryDelayMs, useSSL, nodePriority } = options;
16
16
  if (typeof host !== "string" || !/.+/.test(host)) {
17
17
  throw new TypeError('Node option "host" must be present and be a non-empty string.');
18
18
  }
@@ -36,10 +36,10 @@ function nodeCheck(options) {
36
36
  if (typeof enableSessionResumeOption !== "undefined" && typeof enableSessionResumeOption !== "boolean") {
37
37
  throw new TypeError('Node option "enableSessionResumeOption" must be a boolean.');
38
38
  }
39
- // Validate the sessionTimeoutMs option
40
- // The sessionTimeoutMs option must be a number or undefined.
41
- if (typeof sessionTimeoutMs !== "undefined" && typeof sessionTimeoutMs !== "number") {
42
- throw new TypeError('Node option "sessionTimeoutMs" must be a number.');
39
+ // Validate the sessionTimeoutSeconds option
40
+ // The sessionTimeoutSeconds option must be a number or undefined.
41
+ if (typeof sessionTimeoutSeconds !== "undefined" && typeof sessionTimeoutSeconds !== "number") {
42
+ throw new TypeError('Node option "sessionTimeoutSeconds" must be a number.');
43
43
  }
44
44
  // Validate the maxRetryAttempts option
45
45
  // The maxRetryAttempts option must be a number or undefined.
@@ -12,7 +12,7 @@ function playerCheck(options) {
12
12
  throw new TypeError("PlayerOptions must not be empty.");
13
13
  }
14
14
  // Get the guild ID, node, selfDeafen, selfMute, textChannelId, voiceChannelId, and volume from the options.
15
- const { guildId, node, selfDeafen, selfMute, textChannelId, voiceChannelId, volume } = options;
15
+ const { guildId, nodeIdentifier, selfDeafen, selfMute, textChannelId, voiceChannelId, volume } = options;
16
16
  // Validate the guild ID option
17
17
  // The guild ID option must be a non-empty string.
18
18
  if (!/^\d+$/.test(guildId)) {
@@ -20,8 +20,8 @@ function playerCheck(options) {
20
20
  }
21
21
  // Validate the node option
22
22
  // The node option must be a string.
23
- if (node && typeof node !== "string") {
24
- throw new TypeError('Player option "node" must be a non-empty string.');
23
+ if (nodeIdentifier && typeof nodeIdentifier !== "string") {
24
+ throw new TypeError('Player option "nodeIdentifier" must be a non-empty string.');
25
25
  }
26
26
  // Validate the selfDeafen option
27
27
  // The selfDeafen option must be a boolean.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magmastream",
3
- "version": "2.9.0-dev.40",
3
+ "version": "2.9.0-dev.42",
4
4
  "description": "A user-friendly Lavalink client designed for NodeJS.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",