magmastream 2.9.3-dev.23 → 2.9.3-dev.25

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
@@ -915,6 +915,10 @@ interface ManagerOptions {
915
915
  * @default UseNodeOptions.LeastPlayers
916
916
  */
917
917
  useNode?: UseNodeOptions.LeastLoad | UseNodeOptions.LeastPlayers;
918
+ /** Whether the manager should listen to SIGINT and SIGTERM events.
919
+ * @default true
920
+ */
921
+ listenToSIGEvents?: boolean;
918
922
  /**
919
923
  * Function to send data to the websocket.
920
924
  * @param id The ID of the node to send the data to.
@@ -3151,6 +3155,7 @@ declare class Manager extends EventEmitter {
3151
3155
  private get priorityNode();
3152
3156
  protected send(packet: GatewayVoiceStateUpdate): unknown;
3153
3157
  protected getUserFromCache(id: string): AnyUser | undefined;
3158
+ protected getGuildFromCache(id: string): AnyGuild | undefined;
3154
3159
  sendPacket(packet: GatewayVoiceStateUpdate): unknown;
3155
3160
  /**
3156
3161
  * Resolves a PortableUser or ID to a real user object.
@@ -3703,6 +3708,7 @@ declare class DiscordenoManager extends Manager {
3703
3708
  * Uses user-provided cache getter if available, otherwise falls back to minimal info.
3704
3709
  */
3705
3710
  resolveUser(user: AnyUser | string): Promise<User$2 | AnyUser>;
3711
+ resolveGuild(guildId: string): AnyGuild;
3706
3712
  }
3707
3713
 
3708
3714
  /**
@@ -95,6 +95,7 @@ class Manager extends events_1.EventEmitter {
95
95
  deleteDestroyedPlayers: options.stateStorage?.deleteDestroyedPlayers ?? true,
96
96
  },
97
97
  autoPlaySearchPlatforms: options.autoPlaySearchPlatforms ?? [Enums_1.AutoPlayPlatform.YouTube],
98
+ listenToSIGEvents: options.listenToSIGEvents ?? true,
98
99
  send: this._send,
99
100
  };
100
101
  Utils_1.AutoPlayUtils.init(this);
@@ -102,49 +103,51 @@ class Manager extends events_1.EventEmitter {
102
103
  for (const nodeOptions of this.options.nodes)
103
104
  new Node_1.Node(this, nodeOptions);
104
105
  }
105
- process.on("SIGINT", async () => {
106
- console.warn("\x1b[33mSIGINT received! Graceful shutdown initiated...\x1b[0m");
107
- try {
108
- await this.handleShutdown();
109
- console.warn("\x1b[32mShutdown complete. Waiting for Node.js event loop to empty...\x1b[0m");
110
- // Prevent forced exit by Windows
111
- setTimeout(() => {
106
+ if (this.options.listenToSIGEvents) {
107
+ process.on("SIGINT", async () => {
108
+ console.warn("\x1b[33mSIGINT received! Graceful shutdown initiated...\x1b[0m");
109
+ try {
110
+ await this.handleShutdown();
111
+ console.warn("\x1b[32mShutdown complete. Waiting for Node.js event loop to empty...\x1b[0m");
112
+ // Prevent forced exit by Windows
113
+ setTimeout(() => {
114
+ process.exit(0);
115
+ }, 2000);
116
+ }
117
+ catch (err) {
118
+ const error = err instanceof MagmastreamError_1.MagmaStreamError
119
+ ? err
120
+ : new MagmastreamError_1.MagmaStreamError({
121
+ code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
122
+ message: "An unknown error occurred.",
123
+ cause: err,
124
+ context: { stage: "SIGINT" },
125
+ });
126
+ console.error(error);
127
+ process.exit(1);
128
+ }
129
+ });
130
+ process.on("SIGTERM", async () => {
131
+ console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
132
+ try {
133
+ await this.handleShutdown();
134
+ console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
112
135
  process.exit(0);
113
- }, 2000);
114
- }
115
- catch (err) {
116
- const error = err instanceof MagmastreamError_1.MagmaStreamError
117
- ? err
118
- : new MagmastreamError_1.MagmaStreamError({
119
- code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
120
- message: "An unknown error occurred.",
121
- cause: err,
122
- context: { stage: "SIGINT" },
123
- });
124
- console.error(error);
125
- process.exit(1);
126
- }
127
- });
128
- process.on("SIGTERM", async () => {
129
- console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
130
- try {
131
- await this.handleShutdown();
132
- console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
133
- process.exit(0);
134
- }
135
- catch (err) {
136
- const error = err instanceof MagmastreamError_1.MagmaStreamError
137
- ? err
138
- : new MagmastreamError_1.MagmaStreamError({
139
- code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
140
- message: "An unknown error occurred.",
141
- cause: err,
142
- context: { stage: "SIGTERM" },
143
- });
144
- console.error(error);
145
- process.exit(1);
146
- }
147
- });
136
+ }
137
+ catch (err) {
138
+ const error = err instanceof MagmastreamError_1.MagmaStreamError
139
+ ? err
140
+ : new MagmastreamError_1.MagmaStreamError({
141
+ code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
142
+ message: "An unknown error occurred.",
143
+ cause: err,
144
+ context: { stage: "SIGTERM" },
145
+ });
146
+ console.error(error);
147
+ process.exit(1);
148
+ }
149
+ });
150
+ }
148
151
  }
149
152
  /**
150
153
  * Initiates the Manager.
@@ -527,10 +530,10 @@ class Manager extends events_1.EventEmitter {
527
530
  });
528
531
  // Read guild directories inside players base dir
529
532
  const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
530
- for (const dirent of guildDirs) {
531
- if (!dirent.isDirectory())
533
+ for (const file of guildDirs) {
534
+ if (!file.isDirectory())
532
535
  continue;
533
- const guildId = dirent.name;
536
+ const guildId = file.name;
534
537
  const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
535
538
  try {
536
539
  await promises_1.default.access(stateFilePath);
@@ -541,11 +544,9 @@ class Manager extends events_1.EventEmitter {
541
544
  const hasGuild = this.resolveGuild(state.guildId);
542
545
  if (!hasGuild)
543
546
  continue;
544
- const lavaPlayer = await node.rest.getPlayer(state.guildId);
545
- if (!lavaPlayer) {
546
- await this.destroy(state.guildId);
547
+ const lavaPlayer = (await node.rest.get(`/v4/sessions/${state.node.sessionId}/players/${state.guildId}`));
548
+ if (!lavaPlayer)
547
549
  continue;
548
- }
549
550
  const playerOptions = {
550
551
  guildId: state.options.guildId,
551
552
  textChannelId: state.options.textChannelId,
@@ -556,23 +557,8 @@ class Manager extends events_1.EventEmitter {
556
557
  applyVolumeAsFilter: state.options.applyVolumeAsFilter,
557
558
  pauseOnDisconnect: state.options.pauseOnDisconnect,
558
559
  };
559
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
560
560
  const player = this.create(playerOptions);
561
- player.connect();
562
- await player.node.rest.updatePlayer({
563
- guildId: state.options.guildId,
564
- data: {
565
- voice: {
566
- token: state.voiceState.event.token,
567
- endpoint: state.voiceState.event.endpoint,
568
- sessionId: state.voiceState.sessionId,
569
- channelId: state.voiceState.channelId,
570
- },
571
- },
572
- });
573
- const tracks = [];
574
- const currentTrack = state.queue.current;
575
- const queueTracks = state.queue.tracks;
561
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
576
562
  if (state.isAutoplay) {
577
563
  const savedUser = state.data.clientUser;
578
564
  if (savedUser) {
@@ -584,6 +570,9 @@ class Manager extends events_1.EventEmitter {
584
570
  if (savedNowPlayingMessage) {
585
571
  player.setNowPlayingMessage(savedNowPlayingMessage);
586
572
  }
573
+ const tracks = [];
574
+ const currentTrack = state.queue.current;
575
+ const queueTracks = state.queue.tracks;
587
576
  if (lavaPlayer.track) {
588
577
  await player.queue.clear();
589
578
  if (currentTrack) {
@@ -644,7 +633,6 @@ class Manager extends events_1.EventEmitter {
644
633
  else {
645
634
  await player.queue.clearPrevious();
646
635
  }
647
- await player.pause(state.paused);
648
636
  if (state.trackRepeat)
649
637
  player.setTrackRepeat(true);
650
638
  if (state.queueRepeat)
@@ -690,6 +678,13 @@ class Manager extends events_1.EventEmitter {
690
678
  filterActions[filter](true);
691
679
  }
692
680
  }
681
+ player.connect();
682
+ if (lavaPlayer.track) {
683
+ await player.play(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay), {
684
+ startTime: lavaPlayer.state.position ?? 0,
685
+ });
686
+ }
687
+ await player.pause(state.paused);
693
688
  try {
694
689
  await promises_1.default.rm(Utils_1.PlayerUtils.getPlayerStatePath(guildId), { force: true });
695
690
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state folder for guild ${guildId}`);
@@ -731,10 +726,9 @@ class Manager extends events_1.EventEmitter {
731
726
  const hasGuild = this.resolveGuild(guildId);
732
727
  if (!hasGuild)
733
728
  continue;
734
- const lavaPlayer = await node.rest.getPlayer(state.guildId);
735
- if (!lavaPlayer) {
729
+ const lavaPlayer = (await node.rest.get(`/v4/sessions/${state.node.sessionId}/players/${state.guildId}`));
730
+ if (!lavaPlayer)
736
731
  await this.destroy(guildId);
737
- }
738
732
  const playerOptions = {
739
733
  guildId: state.options.guildId,
740
734
  textChannelId: state.options.textChannelId,
@@ -745,24 +739,8 @@ class Manager extends events_1.EventEmitter {
745
739
  applyVolumeAsFilter: state.options.applyVolumeAsFilter,
746
740
  pauseOnDisconnect: state.options.pauseOnDisconnect,
747
741
  };
748
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
749
742
  const player = this.create(playerOptions);
750
- player.connect();
751
- await player.node.rest.updatePlayer({
752
- guildId: state.options.guildId,
753
- data: {
754
- voice: {
755
- token: state.voiceState.event.token,
756
- endpoint: state.voiceState.event.endpoint,
757
- sessionId: state.voiceState.sessionId,
758
- channelId: state.voiceState.channelId,
759
- },
760
- },
761
- });
762
- // Rest of the player state restoration code (tracks, filters, etc.)
763
- const tracks = [];
764
- const currentTrack = state.queue.current;
765
- const queueTracks = state.queue.tracks;
743
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
766
744
  if (state.isAutoplay) {
767
745
  const savedUser = state.data.clientUser;
768
746
  if (savedUser) {
@@ -774,6 +752,10 @@ class Manager extends events_1.EventEmitter {
774
752
  if (savedNowPlayingMessage) {
775
753
  player.setNowPlayingMessage(savedNowPlayingMessage);
776
754
  }
755
+ // Rest of the player state restoration code (tracks, filters, etc.)
756
+ const tracks = [];
757
+ const currentTrack = state.queue.current;
758
+ const queueTracks = state.queue.tracks;
777
759
  if (lavaPlayer.track) {
778
760
  await player.queue.clear();
779
761
  if (currentTrack) {
@@ -840,7 +822,6 @@ class Manager extends events_1.EventEmitter {
840
822
  else {
841
823
  await player.queue.clearPrevious();
842
824
  }
843
- await player.pause(state.paused);
844
825
  if (state.trackRepeat)
845
826
  player.setTrackRepeat(true);
846
827
  if (state.queueRepeat)
@@ -887,6 +868,13 @@ class Manager extends events_1.EventEmitter {
887
868
  filterActions[filter](true);
888
869
  }
889
870
  }
871
+ player.connect();
872
+ if (lavaPlayer.track) {
873
+ await player.play(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay), {
874
+ startTime: lavaPlayer.state.position ?? 0,
875
+ });
876
+ }
877
+ await player.pause(state.paused);
890
878
  // After processing, delete the Redis key
891
879
  await this.redis.del(key);
892
880
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
@@ -1352,6 +1340,9 @@ class Manager extends events_1.EventEmitter {
1352
1340
  getUserFromCache(id) {
1353
1341
  return this._getUser?.(id);
1354
1342
  }
1343
+ getGuildFromCache(id) {
1344
+ return this._getGuild?.(id);
1345
+ }
1355
1346
  sendPacket(packet) {
1356
1347
  return this.send(packet);
1357
1348
  }
@@ -127,7 +127,8 @@ class Node {
127
127
  }
128
128
  getNodeSessionPath() {
129
129
  const safeId = String(this.options.identifier).replace(/[^a-zA-Z0-9._-]/g, "_");
130
- return path_1.default.join(this.getNodeSessionsDir(), `${safeId}.txt`);
130
+ const clusterId = String(this.manager.options.clusterId ?? 0);
131
+ return path_1.default.join(this.getNodeSessionsDir(), `${safeId}__${clusterId}.txt`);
131
132
  }
132
133
  /**
133
134
  * Loads session IDs from the sessionIds.json file if it exists.
@@ -486,10 +487,11 @@ class Node {
486
487
  case "ready":
487
488
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Node message: ${Utils_1.JSONUtils.safe(payload, 2)}`);
488
489
  this.rest.setSessionId(payload.sessionId);
490
+ const hadPreviousSession = this.sessionId && this.sessionId !== payload.sessionId;
489
491
  this.sessionId = payload.sessionId;
490
492
  await this.updateSessionId();
491
493
  this.info = await this.fetchInfo();
492
- if (payload.resumed) {
494
+ if (payload.resumed || !hadPreviousSession) {
493
495
  await this.manager.loadPlayerStates(this.options.identifier);
494
496
  }
495
497
  if (this.options.enableSessionResumeOption) {
@@ -684,7 +684,6 @@ class PlayerUtils {
684
684
  const snapshot = {
685
685
  options: player.options,
686
686
  voiceState: player.voiceState,
687
- clusterId: player.clusterId,
688
687
  guildId: player.guildId,
689
688
  voiceChannelId: player.voiceChannelId ?? null,
690
689
  textChannelId: player.textChannelId ?? null,
@@ -69,5 +69,9 @@ class DiscordenoManager extends Manager_1.Manager {
69
69
  username: typeof user === "string" ? undefined : user.username,
70
70
  };
71
71
  }
72
+ resolveGuild(guildId) {
73
+ // Try user-provided cache getter
74
+ return this.getGuildFromCache(guildId);
75
+ }
72
76
  }
73
77
  exports.DiscordenoManager = DiscordenoManager;
package/package.json CHANGED
@@ -1,107 +1,107 @@
1
1
  {
2
- "name": "magmastream",
3
- "version": "2.9.3-dev.23",
4
- "description": "A user-friendly Lavalink client designed for NodeJS.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist"
9
- ],
10
- "scripts": {
11
- "build": "tsc",
12
- "types": "rtb --dist dist",
13
- "format": "prettier --write .",
14
- "format:check": "prettier --check .",
15
- "lint": "eslint \"src/**/*.{ts,js}\"",
16
- "lint:fix": "eslint --fix \"src/**/*.{ts,js}\"",
17
- "ci": "run-s format:check lint build types",
18
- "release:dev": "npm run format && npm run lint:fix && npm run ci && npm version prerelease --preid=dev && git push && npm publish --tag dev"
19
- },
20
- "devDependencies": {
21
- "@favware/rollup-type-bundler": "^4.0.0",
22
- "@types/jsdom": "^27.0.0",
23
- "@types/lodash": "^4.17.24",
24
- "@types/node": "^25.3.0",
25
- "@types/ws": "^8.18.1",
26
- "@typescript-eslint/eslint-plugin": "^8.56.0",
27
- "@typescript-eslint/parser": "^8.56.0",
28
- "eslint": "^10.0.1",
29
- "npm-run-all": "^4.1.5",
30
- "prettier": "^3.8.1",
31
- "typedoc": "^0.28.17",
32
- "typedoc-plugin-no-inherit": "^1.6.1",
33
- "typescript": "^5.9.3"
34
- },
35
- "dependencies": {
36
- "@discordjs/collection": "^2.1.1",
37
- "axios": "^1.13.5",
38
- "events": "^3.3.0",
39
- "ioredis": "^5.9.3",
40
- "jsdom": "^28.1.0",
41
- "lodash": "^4.17.23",
42
- "safe-stable-stringify": "^2.5.0",
43
- "tslib": "^2.8.1",
44
- "ws": "^8.19.0"
45
- },
46
- "optionalDependencies": {
47
- "discord.js": "14.x",
48
- "discordeno": "21.x",
49
- "eris": "0.18.x",
50
- "oceanic.js": "^1.13.0",
51
- "seyfert": "4.x"
52
- },
53
- "overrides": {
54
- "undici": "^6.23.0"
55
- },
56
- "engines": {
57
- "node": ">=20.19.0"
58
- },
59
- "eslintConfig": {
60
- "root": true,
61
- "parser": "@typescript-eslint/parser",
62
- "plugins": [
63
- "@typescript-eslint"
64
- ],
65
- "rules": {
66
- "object-curly-spacing": [
67
- "error",
68
- "always"
69
- ]
70
- },
71
- "extends": [
72
- "eslint:recommended",
73
- "plugin:@typescript-eslint/recommended"
74
- ]
75
- },
76
- "keywords": [
77
- "lavalink client",
78
- "wrapper",
79
- "typescript",
80
- "discord.js",
81
- "node.js",
82
- "java",
83
- "javascript",
84
- "audio streaming",
85
- "music bot",
86
- "voice chat",
87
- "discord integration",
88
- "high performance",
89
- "scalable",
90
- "easy-to-use",
91
- "feature-rich",
92
- "cross-platform",
93
- "seamless integration",
94
- "community support",
95
- "documentation",
96
- "open-source",
97
- "lavalink",
98
- "magmastream"
99
- ],
100
- "repository": {
101
- "type": "git",
102
- "url": "git+https://gitryx.com/MagmaStream/magmastream.git#main"
103
- },
104
- "homepage": "https://docs.magmastream.com",
105
- "author": "Abel Purnwasy",
106
- "license": "Apache-2.0"
2
+ "name": "magmastream",
3
+ "version": "2.9.3-dev.25",
4
+ "description": "A user-friendly Lavalink client designed for NodeJS.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "types": "rtb --dist dist",
13
+ "format": "prettier --write .",
14
+ "format:check": "prettier --check .",
15
+ "lint": "eslint \"src/**/*.{ts,js}\"",
16
+ "lint:fix": "eslint --fix \"src/**/*.{ts,js}\"",
17
+ "ci": "run-s format:check lint build types",
18
+ "release:dev": "npm run format && npm run lint:fix && npm run ci && npm version prerelease --preid=dev && git push && npm publish --tag dev"
19
+ },
20
+ "devDependencies": {
21
+ "@favware/rollup-type-bundler": "^4.0.0",
22
+ "@types/jsdom": "^28.0.0",
23
+ "@types/lodash": "^4.17.24",
24
+ "@types/node": "^25.3.0",
25
+ "@types/ws": "^8.18.1",
26
+ "@typescript-eslint/eslint-plugin": "^8.56.0",
27
+ "@typescript-eslint/parser": "^8.56.0",
28
+ "eslint": "^10.0.2",
29
+ "npm-run-all": "^4.1.5",
30
+ "prettier": "^3.8.1",
31
+ "typedoc": "^0.28.17",
32
+ "typedoc-plugin-no-inherit": "^1.6.1",
33
+ "typescript": "^5.9.3"
34
+ },
35
+ "dependencies": {
36
+ "@discordjs/collection": "^2.1.1",
37
+ "axios": "^1.13.6",
38
+ "events": "^3.3.0",
39
+ "ioredis": "^5.10.0",
40
+ "jsdom": "^28.1.0",
41
+ "lodash": "^4.17.23",
42
+ "safe-stable-stringify": "^2.5.0",
43
+ "tslib": "^2.8.1",
44
+ "ws": "^8.19.0"
45
+ },
46
+ "optionalDependencies": {
47
+ "discord.js": "14.x",
48
+ "discordeno": "21.x",
49
+ "eris": "0.18.x",
50
+ "oceanic.js": "1.13.x",
51
+ "seyfert": "4.x"
52
+ },
53
+ "overrides": {
54
+ "undici": "^7.0.0"
55
+ },
56
+ "engines": {
57
+ "node": ">=20.19.0"
58
+ },
59
+ "eslintConfig": {
60
+ "root": true,
61
+ "parser": "@typescript-eslint/parser",
62
+ "plugins": [
63
+ "@typescript-eslint"
64
+ ],
65
+ "rules": {
66
+ "object-curly-spacing": [
67
+ "error",
68
+ "always"
69
+ ]
70
+ },
71
+ "extends": [
72
+ "eslint:recommended",
73
+ "plugin:@typescript-eslint/recommended"
74
+ ]
75
+ },
76
+ "keywords": [
77
+ "lavalink client",
78
+ "wrapper",
79
+ "typescript",
80
+ "discord.js",
81
+ "node.js",
82
+ "java",
83
+ "javascript",
84
+ "audio streaming",
85
+ "music bot",
86
+ "voice chat",
87
+ "discord integration",
88
+ "high performance",
89
+ "scalable",
90
+ "easy-to-use",
91
+ "feature-rich",
92
+ "cross-platform",
93
+ "seamless integration",
94
+ "community support",
95
+ "documentation",
96
+ "open-source",
97
+ "lavalink",
98
+ "magmastream"
99
+ ],
100
+ "repository": {
101
+ "type": "git",
102
+ "url": "git+https://gitryx.com/MagmaStream/magmastream.git#main"
103
+ },
104
+ "homepage": "https://docs.magmastream.com",
105
+ "author": "Abel Purnwasy",
106
+ "license": "Apache-2.0"
107
107
  }