magmastream 2.9.3-dev.21 → 2.9.3-dev.23
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 +7 -7
- package/dist/structures/Manager.js +8 -104
- package/dist/structures/Node.js +7 -5
- package/dist/structures/Player.js +22 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -859,7 +859,7 @@ declare abstract class Plugin {
|
|
|
859
859
|
interface ManagerOptions {
|
|
860
860
|
/** The state storage options.
|
|
861
861
|
*
|
|
862
|
-
* @default { type: StateStorageType.Collection,
|
|
862
|
+
* @default { type: StateStorageType.Collection, deleteDestroyedPlayers: true }
|
|
863
863
|
*/
|
|
864
864
|
stateStorage?: StateStorageOptions;
|
|
865
865
|
/** Enable priority mode over least player count or load balancing?
|
|
@@ -945,7 +945,7 @@ interface StateStorageOptions {
|
|
|
945
945
|
type: StateStorageType;
|
|
946
946
|
redisConfig?: RedisConfig;
|
|
947
947
|
jsonConfig?: JsonConfig;
|
|
948
|
-
|
|
948
|
+
deleteDestroyedPlayers?: boolean;
|
|
949
949
|
}
|
|
950
950
|
/**
|
|
951
951
|
* Node Options
|
|
@@ -2435,6 +2435,11 @@ declare class Player {
|
|
|
2435
2435
|
* @returns {Promise<void>} - A promise that resolves when the voice receiver error is handled.
|
|
2436
2436
|
*/
|
|
2437
2437
|
private onVoiceReceiverError;
|
|
2438
|
+
/**
|
|
2439
|
+
* Updates the voice state for the player.
|
|
2440
|
+
* @returns {Promise<void>} - A promise that resolves when the voice state is updated.
|
|
2441
|
+
*/
|
|
2442
|
+
updateVoice(): Promise<void>;
|
|
2438
2443
|
}
|
|
2439
2444
|
|
|
2440
2445
|
/** Handles the requests sent to the Lavalink REST API. */
|
|
@@ -3101,11 +3106,6 @@ declare class Manager extends EventEmitter {
|
|
|
3101
3106
|
* @emits {playerDisconnect} - Emits a player disconnect event if the channel ID is null.
|
|
3102
3107
|
*/
|
|
3103
3108
|
private handleVoiceStateUpdate;
|
|
3104
|
-
/**
|
|
3105
|
-
* Cleans up inactive players by removing their state files from the file system.
|
|
3106
|
-
* This is done to prevent stale state files from accumulating on the file system.
|
|
3107
|
-
*/
|
|
3108
|
-
cleanupInactivePlayers(): Promise<void>;
|
|
3109
3109
|
/**
|
|
3110
3110
|
* Cleans up an inactive player by removing its state data.
|
|
3111
3111
|
* This is done to prevent stale state data from accumulating.
|
|
@@ -92,7 +92,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
92
92
|
stateStorage: {
|
|
93
93
|
...options.stateStorage,
|
|
94
94
|
type: options.stateStorage?.type ?? Enums_1.StateStorageType.Memory,
|
|
95
|
-
|
|
95
|
+
deleteDestroyedPlayers: options.stateStorage?.deleteDestroyedPlayers ?? true,
|
|
96
96
|
},
|
|
97
97
|
autoPlaySearchPlatforms: options.autoPlaySearchPlatforms ?? [Enums_1.AutoPlayPlatform.YouTube],
|
|
98
98
|
send: this._send,
|
|
@@ -558,6 +558,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
558
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();
|
|
561
562
|
await player.node.rest.updatePlayer({
|
|
562
563
|
guildId: state.options.guildId,
|
|
563
564
|
data: {
|
|
@@ -565,11 +566,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
565
566
|
token: state.voiceState.event.token,
|
|
566
567
|
endpoint: state.voiceState.event.endpoint,
|
|
567
568
|
sessionId: state.voiceState.sessionId,
|
|
568
|
-
channelId: state.voiceState.
|
|
569
|
+
channelId: state.voiceState.channelId,
|
|
569
570
|
},
|
|
570
571
|
},
|
|
571
572
|
});
|
|
572
|
-
player.connect();
|
|
573
573
|
const tracks = [];
|
|
574
574
|
const currentTrack = state.queue.current;
|
|
575
575
|
const queueTracks = state.queue.tracks;
|
|
@@ -747,6 +747,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
747
747
|
};
|
|
748
748
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
|
|
749
749
|
const player = this.create(playerOptions);
|
|
750
|
+
player.connect();
|
|
750
751
|
await player.node.rest.updatePlayer({
|
|
751
752
|
guildId: state.options.guildId,
|
|
752
753
|
data: {
|
|
@@ -754,11 +755,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
754
755
|
token: state.voiceState.event.token,
|
|
755
756
|
endpoint: state.voiceState.event.endpoint,
|
|
756
757
|
sessionId: state.voiceState.sessionId,
|
|
757
|
-
channelId: state.voiceState.
|
|
758
|
+
channelId: state.voiceState.channelId,
|
|
758
759
|
},
|
|
759
760
|
},
|
|
760
761
|
});
|
|
761
|
-
player.connect();
|
|
762
762
|
// Rest of the player state restoration code (tracks, filters, etc.)
|
|
763
763
|
const tracks = [];
|
|
764
764
|
const currentTrack = state.queue.current;
|
|
@@ -949,8 +949,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
949
949
|
}
|
|
950
950
|
});
|
|
951
951
|
await Promise.allSettled(savePromises);
|
|
952
|
-
if (this.options.stateStorage.deleteInactivePlayers)
|
|
953
|
-
await this.cleanupInactivePlayers();
|
|
954
952
|
setTimeout(() => {
|
|
955
953
|
console.warn("\x1b[32m%s\x1b[0m", "MAGMASTREAM INFO: Shutting down complete, exiting...");
|
|
956
954
|
process.exit(0);
|
|
@@ -1080,15 +1078,8 @@ class Manager extends events_1.EventEmitter {
|
|
|
1080
1078
|
player.voiceState.event = update;
|
|
1081
1079
|
const sessionId = player.voiceState.sessionId;
|
|
1082
1080
|
const channelId = player.voiceState.channelId;
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${token} | endpoint ${endpoint} | sessionId ${sessionId} | channelId ${channelId}`);
|
|
1086
|
-
if (!sessionId || !channelId)
|
|
1087
|
-
return;
|
|
1088
|
-
await player.node.rest.updatePlayer({
|
|
1089
|
-
guildId: player.guildId,
|
|
1090
|
-
data: { voice: { token, endpoint, sessionId, channelId } },
|
|
1091
|
-
});
|
|
1081
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${update.token} | endpoint ${update.endpoint} | sessionId ${sessionId} | channelId ${channelId}`);
|
|
1082
|
+
await player.updateVoice();
|
|
1092
1083
|
}
|
|
1093
1084
|
/**
|
|
1094
1085
|
* Handles a voice state update by updating the player's voice channel and session ID if provided, or by disconnecting and destroying the player if the channel ID is null.
|
|
@@ -1115,94 +1106,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1115
1106
|
player.voiceState.channelId = update.channel_id;
|
|
1116
1107
|
player.voiceChannelId = update.channel_id;
|
|
1117
1108
|
player.options.voiceChannelId = update.channel_id;
|
|
1118
|
-
|
|
1119
|
-
const endpoint = player.voiceState.event?.endpoint;
|
|
1120
|
-
if (!token || !endpoint)
|
|
1121
|
-
return;
|
|
1122
|
-
await player.node.rest.updatePlayer({
|
|
1123
|
-
guildId: player.guildId,
|
|
1124
|
-
data: { voice: { token, endpoint, sessionId: update.session_id, channelId: update.channel_id } },
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Cleans up inactive players by removing their state files from the file system.
|
|
1129
|
-
* This is done to prevent stale state files from accumulating on the file system.
|
|
1130
|
-
*/
|
|
1131
|
-
async cleanupInactivePlayers() {
|
|
1132
|
-
switch (this.options.stateStorage.type) {
|
|
1133
|
-
case Enums_1.StateStorageType.JSON:
|
|
1134
|
-
{
|
|
1135
|
-
const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
|
|
1136
|
-
try {
|
|
1137
|
-
await promises_1.default.mkdir(playersBaseDir, { recursive: true });
|
|
1138
|
-
const activeGuildIds = new Set(this.players.keys());
|
|
1139
|
-
// Cleanup inactive guild directories inside playersBaseDir
|
|
1140
|
-
const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
|
|
1141
|
-
for (const dirent of guildDirs) {
|
|
1142
|
-
if (!dirent.isDirectory())
|
|
1143
|
-
continue;
|
|
1144
|
-
const guildId = dirent.name;
|
|
1145
|
-
if (!activeGuildIds.has(guildId)) {
|
|
1146
|
-
const guildPath = Utils_1.PlayerUtils.getGuildDir(guildId);
|
|
1147
|
-
await promises_1.default.rm(guildPath, { recursive: true, force: true });
|
|
1148
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
catch (err) {
|
|
1153
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error cleaning up inactive JSON players: ${err}`);
|
|
1154
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1155
|
-
? err
|
|
1156
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
1157
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED,
|
|
1158
|
-
message: "Error cleaning up inactive players.",
|
|
1159
|
-
cause: err,
|
|
1160
|
-
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1161
|
-
});
|
|
1162
|
-
console.error(error);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
break;
|
|
1166
|
-
case Enums_1.StateStorageType.Redis:
|
|
1167
|
-
{
|
|
1168
|
-
const prefix = Utils_1.PlayerUtils.getRedisKey();
|
|
1169
|
-
const pattern = `${prefix}queue:*:current`;
|
|
1170
|
-
try {
|
|
1171
|
-
const stream = this.redis.scanStream({
|
|
1172
|
-
match: pattern,
|
|
1173
|
-
count: 100,
|
|
1174
|
-
});
|
|
1175
|
-
for await (const keys of stream) {
|
|
1176
|
-
for (const key of keys) {
|
|
1177
|
-
// Extract guildId from queue key
|
|
1178
|
-
const match = key.match(new RegExp(`^${prefix}queue:(.+):current$`));
|
|
1179
|
-
if (!match)
|
|
1180
|
-
continue;
|
|
1181
|
-
const guildId = match[1];
|
|
1182
|
-
// If player is not active in memory, clean up all keys
|
|
1183
|
-
if (!this.players.has(guildId)) {
|
|
1184
|
-
await this.redis.del(`${prefix}playerstore:${guildId}`, `${prefix}queue:${guildId}:current`, `${prefix}queue:${guildId}:tracks`, `${prefix}queue:${guildId}:previous`);
|
|
1185
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Cleaned inactive Redis player data: ${guildId}`);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
catch (err) {
|
|
1191
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1192
|
-
? err
|
|
1193
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
1194
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
1195
|
-
message: "Error saving player state.",
|
|
1196
|
-
cause: err,
|
|
1197
|
-
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1198
|
-
});
|
|
1199
|
-
console.error(error);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
break;
|
|
1203
|
-
default:
|
|
1204
|
-
break;
|
|
1205
|
-
}
|
|
1109
|
+
await player.updateVoice();
|
|
1206
1110
|
}
|
|
1207
1111
|
/**
|
|
1208
1112
|
* Cleans up an inactive player by removing its state data.
|
package/dist/structures/Node.js
CHANGED
|
@@ -99,9 +99,7 @@ class Node {
|
|
|
99
99
|
this.createReadmeFile();
|
|
100
100
|
break;
|
|
101
101
|
case Enums_1.StateStorageType.Redis:
|
|
102
|
-
this.redisPrefix =
|
|
103
|
-
? this.manager.options.stateStorage.redisConfig.prefix
|
|
104
|
-
: (this.manager.options.stateStorage.redisConfig.prefix ?? "magmastream:");
|
|
102
|
+
this.redisPrefix = Utils_1.PlayerUtils.getRedisKey();
|
|
105
103
|
break;
|
|
106
104
|
}
|
|
107
105
|
}
|
|
@@ -125,7 +123,7 @@ class Node {
|
|
|
125
123
|
return `${this.redisPrefix}node:sessionIds`;
|
|
126
124
|
}
|
|
127
125
|
getNodeSessionsDir() {
|
|
128
|
-
return path_1.default.join(process.cwd(), "magmastream", "sessionData", "
|
|
126
|
+
return path_1.default.join(process.cwd(), "magmastream", "sessionData", "nodeSessions");
|
|
129
127
|
}
|
|
130
128
|
getNodeSessionPath() {
|
|
131
129
|
const safeId = String(this.options.identifier).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
@@ -1173,8 +1171,12 @@ class Node {
|
|
|
1173
1171
|
* @private
|
|
1174
1172
|
*/
|
|
1175
1173
|
createReadmeFile() {
|
|
1176
|
-
const
|
|
1174
|
+
const baseDir = path_1.default.join(process.cwd(), "magmastream");
|
|
1175
|
+
const readmeFilePath = path_1.default.join(baseDir, "README.md");
|
|
1177
1176
|
const message = "Please do NOT delete the magmastream/ folder as it is used to store player data for autoresume etc.";
|
|
1177
|
+
if (!fs_1.default.existsSync(baseDir)) {
|
|
1178
|
+
fs_1.default.mkdirSync(baseDir, { recursive: true });
|
|
1179
|
+
}
|
|
1178
1180
|
if (!fs_1.default.existsSync(readmeFilePath)) {
|
|
1179
1181
|
fs_1.default.writeFileSync(readmeFilePath, message, "utf-8");
|
|
1180
1182
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[NODE] Created README file at: ${readmeFilePath}`);
|
|
@@ -281,7 +281,7 @@ class Player {
|
|
|
281
281
|
this.nowPlayingMessage = undefined;
|
|
282
282
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerDestroy, this);
|
|
283
283
|
const deleted = this.manager.players.delete(this.guildId);
|
|
284
|
-
if (this.manager.options.stateStorage.
|
|
284
|
+
if (this.manager.options.stateStorage.deleteDestroyedPlayers) {
|
|
285
285
|
await this.manager.cleanupInactivePlayer(this.guildId);
|
|
286
286
|
}
|
|
287
287
|
return deleted;
|
|
@@ -1144,5 +1144,26 @@ class Player {
|
|
|
1144
1144
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver error for player ${this.guildId}: ${error.message}`);
|
|
1145
1145
|
this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverError, this, error);
|
|
1146
1146
|
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Updates the voice state for the player.
|
|
1149
|
+
* @returns {Promise<void>} - A promise that resolves when the voice state is updated.
|
|
1150
|
+
*/
|
|
1151
|
+
async updateVoice() {
|
|
1152
|
+
const vs = this.voiceState;
|
|
1153
|
+
const ev = vs?.event;
|
|
1154
|
+
if (!vs?.channelId || !vs?.sessionId || !ev?.token || !ev?.endpoint)
|
|
1155
|
+
return;
|
|
1156
|
+
await this.node.rest.updatePlayer({
|
|
1157
|
+
guildId: this.options.guildId,
|
|
1158
|
+
data: {
|
|
1159
|
+
voice: {
|
|
1160
|
+
token: ev.token,
|
|
1161
|
+
endpoint: ev.endpoint,
|
|
1162
|
+
sessionId: vs.sessionId,
|
|
1163
|
+
channelId: vs.channelId,
|
|
1164
|
+
},
|
|
1165
|
+
},
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1147
1168
|
}
|
|
1148
1169
|
exports.Player = Player;
|