magmastream 2.9.3-dev.3 → 2.9.3-dev.30
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/config/blockedWords.d.ts +1 -0
- package/dist/index.d.ts +19 -3687
- package/dist/index.js +1 -1
- package/dist/statestorage/JsonQueue.d.ts +173 -0
- package/dist/statestorage/JsonQueue.js +32 -4
- package/dist/statestorage/MemoryQueue.d.ts +154 -0
- package/dist/statestorage/MemoryQueue.js +56 -36
- package/dist/statestorage/RedisQueue.d.ts +178 -0
- package/dist/statestorage/RedisQueue.js +29 -7
- package/dist/structures/Enums.d.ts +310 -0
- package/dist/structures/Enums.js +6 -0
- package/dist/structures/Filters.d.ts +352 -0
- package/dist/structures/Filters.js +5 -4
- package/dist/structures/MagmastreamError.d.ts +14 -0
- package/dist/structures/Manager.d.ts +259 -0
- package/dist/structures/Manager.js +297 -555
- package/dist/structures/Node.d.ts +390 -0
- package/dist/structures/Node.js +98 -143
- package/dist/structures/Player.d.ts +347 -0
- package/dist/structures/Player.js +53 -127
- package/dist/structures/Plugin.d.ts +23 -0
- package/dist/structures/Rest.d.ts +93 -0
- package/dist/structures/Rest.js +41 -21
- package/dist/structures/Types.d.ts +1315 -0
- package/dist/structures/Utils.d.ts +169 -0
- package/dist/structures/Utils.js +145 -71
- package/dist/utils/filtersEqualizers.d.ts +16 -0
- package/dist/utils/managerCheck.d.ts +7 -0
- package/dist/utils/nodeCheck.d.ts +7 -0
- package/dist/utils/playerCheck.d.ts +7 -0
- package/dist/wrappers/discord.js.d.ts +15 -0
- package/dist/wrappers/discord.js.js +19 -4
- package/dist/wrappers/discordeno.d.ts +19 -0
- package/dist/wrappers/discordeno.js +77 -0
- package/dist/wrappers/eris.d.ts +15 -0
- package/dist/wrappers/eris.js +20 -3
- package/dist/wrappers/oceanic.d.ts +15 -0
- package/dist/wrappers/oceanic.js +22 -4
- package/dist/wrappers/seyfert.d.ts +37 -0
- package/dist/wrappers/seyfert.js +25 -1
- package/package.json +106 -98
- package/dist/wrappers/detritus.js +0 -52
|
@@ -28,6 +28,8 @@ class Manager extends events_1.EventEmitter {
|
|
|
28
28
|
initiated = false;
|
|
29
29
|
redis;
|
|
30
30
|
_send;
|
|
31
|
+
_getUser;
|
|
32
|
+
_getGuild;
|
|
31
33
|
loadedPlugins = new Set();
|
|
32
34
|
/**
|
|
33
35
|
* Initiates the Manager class.
|
|
@@ -61,6 +63,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
61
63
|
this.options.clusterId = options.clusterId;
|
|
62
64
|
if (options.send && !this._send)
|
|
63
65
|
this._send = options.send;
|
|
66
|
+
if (options.getUser && !this._getUser)
|
|
67
|
+
this._getUser = options.getUser;
|
|
68
|
+
if (options.getGuild && !this._getGuild)
|
|
69
|
+
this._getGuild = options.getGuild;
|
|
64
70
|
this.options = {
|
|
65
71
|
...options,
|
|
66
72
|
enabledPlugins: options.enabledPlugins ?? [],
|
|
@@ -86,9 +92,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
86
92
|
stateStorage: {
|
|
87
93
|
...options.stateStorage,
|
|
88
94
|
type: options.stateStorage?.type ?? Enums_1.StateStorageType.Memory,
|
|
89
|
-
|
|
95
|
+
deleteDestroyedPlayers: options.stateStorage?.deleteDestroyedPlayers ?? true,
|
|
90
96
|
},
|
|
91
97
|
autoPlaySearchPlatforms: options.autoPlaySearchPlatforms ?? [Enums_1.AutoPlayPlatform.YouTube],
|
|
98
|
+
listenToSIGEvents: options.listenToSIGEvents ?? true,
|
|
92
99
|
send: this._send,
|
|
93
100
|
};
|
|
94
101
|
Utils_1.AutoPlayUtils.init(this);
|
|
@@ -96,49 +103,51 @@ class Manager extends events_1.EventEmitter {
|
|
|
96
103
|
for (const nodeOptions of this.options.nodes)
|
|
97
104
|
new Node_1.Node(this, nodeOptions);
|
|
98
105
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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");
|
|
106
135
|
process.exit(0);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
process.on("SIGTERM", async () => {
|
|
123
|
-
console.warn("\x1b[33mSIGTERM received! Graceful shutdown initiated...\x1b[0m");
|
|
124
|
-
try {
|
|
125
|
-
await this.handleShutdown();
|
|
126
|
-
console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
|
|
127
|
-
process.exit(0);
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
131
|
-
? err
|
|
132
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
133
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
134
|
-
message: "An unknown error occurred.",
|
|
135
|
-
cause: err,
|
|
136
|
-
context: { stage: "SIGTERM" },
|
|
137
|
-
});
|
|
138
|
-
console.error(error);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
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
|
+
}
|
|
142
151
|
}
|
|
143
152
|
/**
|
|
144
153
|
* Initiates the Manager.
|
|
@@ -177,11 +186,15 @@ class Manager extends events_1.EventEmitter {
|
|
|
177
186
|
db: config.db ?? 0,
|
|
178
187
|
});
|
|
179
188
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
189
|
+
const results = await Promise.allSettled([...this.nodes.values()].map(async (node) => {
|
|
190
|
+
await node.connect();
|
|
191
|
+
return node;
|
|
192
|
+
}));
|
|
193
|
+
for (let i = 0; i < results.length; i++) {
|
|
194
|
+
const result = results[i];
|
|
195
|
+
const node = [...this.nodes.values()][i];
|
|
196
|
+
if (result.status === "rejected") {
|
|
197
|
+
const err = result.reason;
|
|
185
198
|
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
186
199
|
? err
|
|
187
200
|
: new MagmastreamError_1.MagmaStreamError({
|
|
@@ -277,9 +290,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
277
290
|
result.playlist.tracks = result.playlist.tracks.map(processTrack);
|
|
278
291
|
}
|
|
279
292
|
}
|
|
280
|
-
const summary = "tracks" in result
|
|
281
|
-
? result.tracks.map((t) => Object.fromEntries(Object.entries(t).filter(([key]) => key !== "requester")))
|
|
282
|
-
: [];
|
|
293
|
+
const summary = "tracks" in result ? result.tracks.map((t) => Object.fromEntries(Object.entries(t).filter(([key]) => key !== "requester"))) : [];
|
|
283
294
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Result search for ${_query.query}: ${Utils_1.JSONUtils.safe(summary, 2)}`);
|
|
284
295
|
return result;
|
|
285
296
|
}
|
|
@@ -466,9 +477,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
466
477
|
case Enums_1.StateStorageType.Redis:
|
|
467
478
|
{
|
|
468
479
|
try {
|
|
469
|
-
const redisKey = `${
|
|
470
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
471
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:${guildId}`;
|
|
480
|
+
const redisKey = `${Utils_1.PlayerUtils.getRedisKey()}playerstore:${guildId}`;
|
|
472
481
|
await this.redis.set(redisKey, JSON.stringify(serializedPlayer));
|
|
473
482
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Player state saved to Redis: ${guildId}`);
|
|
474
483
|
}
|
|
@@ -492,6 +501,155 @@ class Manager extends events_1.EventEmitter {
|
|
|
492
501
|
async sleep(ms) {
|
|
493
502
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
494
503
|
}
|
|
504
|
+
async restorePlayerFromState(node, nodeId, guildId, state, cleanup) {
|
|
505
|
+
if (!state.guildId || state.node?.options?.identifier !== nodeId)
|
|
506
|
+
return;
|
|
507
|
+
const hasGuild = this.resolveGuild(state.guildId);
|
|
508
|
+
if (!hasGuild)
|
|
509
|
+
return;
|
|
510
|
+
const lavaPlayer = (await node.rest.get(`/v4/sessions/${state.node.sessionId}/players/${state.guildId}`));
|
|
511
|
+
if (!lavaPlayer)
|
|
512
|
+
return;
|
|
513
|
+
const playerOptions = {
|
|
514
|
+
guildId: state.options.guildId,
|
|
515
|
+
textChannelId: state.options.textChannelId,
|
|
516
|
+
voiceChannelId: state.options.voiceChannelId,
|
|
517
|
+
selfDeafen: state.options.selfDeafen,
|
|
518
|
+
volume: lavaPlayer.volume || state.options.volume,
|
|
519
|
+
nodeIdentifier: nodeId,
|
|
520
|
+
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
521
|
+
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
522
|
+
};
|
|
523
|
+
const player = this.create(playerOptions);
|
|
524
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId}`);
|
|
525
|
+
if (state.isAutoplay) {
|
|
526
|
+
const savedUser = state.data?.clientUser;
|
|
527
|
+
if (savedUser) {
|
|
528
|
+
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
529
|
+
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const savedNowPlayingMessage = state.data?.nowPlayingMessage;
|
|
533
|
+
if (savedNowPlayingMessage) {
|
|
534
|
+
player.setNowPlayingMessage(savedNowPlayingMessage);
|
|
535
|
+
}
|
|
536
|
+
await this.restoreQueue(node, player, state, lavaPlayer);
|
|
537
|
+
await this.restorePreviousQueue(player, state);
|
|
538
|
+
this.restoreRepeatState(player, state);
|
|
539
|
+
this.restorePlayerData(player, state);
|
|
540
|
+
this.restoreFilters(player, state);
|
|
541
|
+
player.connect();
|
|
542
|
+
if (lavaPlayer.track && state.clusterId !== player.clusterId) {
|
|
543
|
+
const currentTrack = state.queue.current;
|
|
544
|
+
await player.play(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
545
|
+
await player.seek(lavaPlayer.state.position);
|
|
546
|
+
await node.rest.delete(`/v4/sessions/${state.node.sessionId}/players/${state.guildId}`);
|
|
547
|
+
}
|
|
548
|
+
await player.pause(state.paused);
|
|
549
|
+
await cleanup();
|
|
550
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
551
|
+
await this.sleep(1000);
|
|
552
|
+
}
|
|
553
|
+
async restoreQueue(node, player, state, lavaPlayer) {
|
|
554
|
+
const currentTrack = state.queue.current;
|
|
555
|
+
const queueTracks = state.queue.tracks;
|
|
556
|
+
if (lavaPlayer.track) {
|
|
557
|
+
await player.queue.clear();
|
|
558
|
+
if (currentTrack) {
|
|
559
|
+
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
560
|
+
}
|
|
561
|
+
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
562
|
+
if (remainingQueue.length > 0)
|
|
563
|
+
await player.queue.add(remainingQueue);
|
|
564
|
+
player.playing = !lavaPlayer.paused;
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
// No active lavalink track
|
|
568
|
+
if (currentTrack) {
|
|
569
|
+
if (queueTracks.length > 0) {
|
|
570
|
+
await player.queue.clear();
|
|
571
|
+
await player.queue.add(queueTracks);
|
|
572
|
+
}
|
|
573
|
+
await node.trackEnd(player, currentTrack, {
|
|
574
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
575
|
+
type: "TrackEndEvent",
|
|
576
|
+
});
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
// No current track either — check previous
|
|
580
|
+
const previousQueue = await player.queue.getPrevious();
|
|
581
|
+
const lastTrack = previousQueue?.at(-1);
|
|
582
|
+
if (queueTracks.length > 0) {
|
|
583
|
+
await player.queue.clear();
|
|
584
|
+
await player.queue.add(queueTracks);
|
|
585
|
+
}
|
|
586
|
+
if (lastTrack || queueTracks.length > 0) {
|
|
587
|
+
await node.trackEnd(player, lastTrack, {
|
|
588
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
589
|
+
type: "TrackEndEvent",
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async restorePreviousQueue(player, state) {
|
|
594
|
+
if (state.queue.previous.length > 0) {
|
|
595
|
+
const validPrevious = state.queue.previous.filter((t) => t !== null && typeof t.identifier === "string");
|
|
596
|
+
if (validPrevious.length > 0)
|
|
597
|
+
await player.queue.addPrevious(validPrevious);
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
await player.queue.clearPrevious();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
restoreRepeatState(player, state) {
|
|
604
|
+
if (state.trackRepeat)
|
|
605
|
+
player.setTrackRepeat(true);
|
|
606
|
+
if (state.queueRepeat)
|
|
607
|
+
player.setQueueRepeat(true);
|
|
608
|
+
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
609
|
+
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
restorePlayerData(player, state) {
|
|
613
|
+
if (!state.data)
|
|
614
|
+
return;
|
|
615
|
+
for (const [name, value] of Object.entries(state.data)) {
|
|
616
|
+
player.set(name, value);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
restoreFilters(player, state) {
|
|
620
|
+
const filterActions = {
|
|
621
|
+
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
622
|
+
distort: (e) => player.filters.distort(e),
|
|
623
|
+
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
624
|
+
eightD: (e) => player.filters.eightD(e),
|
|
625
|
+
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
626
|
+
nightcore: (e) => player.filters.nightcore(e),
|
|
627
|
+
slowmo: (e) => player.filters.slowmo(e),
|
|
628
|
+
soft: (e) => player.filters.soft(e),
|
|
629
|
+
trebleBass: (e) => player.filters.trebleBass(e),
|
|
630
|
+
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
631
|
+
tv: (e) => player.filters.tv(e),
|
|
632
|
+
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
633
|
+
vaporwave: (e) => player.filters.vaporwave(e),
|
|
634
|
+
pop: (e) => player.filters.pop(e),
|
|
635
|
+
party: (e) => player.filters.party(e),
|
|
636
|
+
earrape: (e) => player.filters.earrape(e),
|
|
637
|
+
electronic: (e) => player.filters.electronic(e),
|
|
638
|
+
radio: (e) => player.filters.radio(e),
|
|
639
|
+
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
640
|
+
tremolo: (e) => player.filters.tremolo(e),
|
|
641
|
+
china: (e) => player.filters.china(e),
|
|
642
|
+
chipmunk: (e) => player.filters.chipmunk(e),
|
|
643
|
+
darthvader: (e) => player.filters.darthvader(e),
|
|
644
|
+
daycore: (e) => player.filters.daycore(e),
|
|
645
|
+
doubletime: (e) => player.filters.doubletime(e),
|
|
646
|
+
demon: (e) => player.filters.demon(e),
|
|
647
|
+
};
|
|
648
|
+
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
649
|
+
if (isEnabled && filterActions[filter])
|
|
650
|
+
filterActions[filter](true);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
495
653
|
/**
|
|
496
654
|
* Loads player states from the JSON file.
|
|
497
655
|
* @param nodeId The ID of the node to load player states from.
|
|
@@ -501,7 +659,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
501
659
|
this.emit(Enums_1.ManagerEventTypes.Debug, "[MANAGER] Loading saved players.");
|
|
502
660
|
const node = this.nodes.get(nodeId);
|
|
503
661
|
if (!node) {
|
|
504
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Tried to load non-existent node: ${nodeId}`);
|
|
505
662
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
506
663
|
code: Enums_1.MagmaStreamErrorCode.MANAGER_NODE_NOT_FOUND,
|
|
507
664
|
message: "Node not found.",
|
|
@@ -510,400 +667,65 @@ class Manager extends events_1.EventEmitter {
|
|
|
510
667
|
}
|
|
511
668
|
switch (this.options.stateStorage.type) {
|
|
512
669
|
case Enums_1.StateStorageType.Memory:
|
|
513
|
-
case Enums_1.StateStorageType.JSON:
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const state = JSON.parse(rawData);
|
|
533
|
-
if (state.clusterId !== this.options.clusterId)
|
|
534
|
-
continue;
|
|
535
|
-
if (!state.guildId || state.node?.options?.identifier !== nodeId)
|
|
536
|
-
continue;
|
|
537
|
-
const lavaPlayer = await node.rest.getPlayer(state.guildId);
|
|
538
|
-
if (!lavaPlayer) {
|
|
539
|
-
await this.destroy(state.guildId);
|
|
540
|
-
continue;
|
|
541
|
-
}
|
|
542
|
-
const playerOptions = {
|
|
543
|
-
guildId: state.options.guildId,
|
|
544
|
-
textChannelId: state.options.textChannelId,
|
|
545
|
-
voiceChannelId: state.options.voiceChannelId,
|
|
546
|
-
selfDeafen: state.options.selfDeafen,
|
|
547
|
-
volume: lavaPlayer.volume || state.options.volume,
|
|
548
|
-
nodeIdentifier: nodeId,
|
|
549
|
-
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
550
|
-
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
551
|
-
};
|
|
552
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
|
|
553
|
-
const player = this.create(playerOptions);
|
|
554
|
-
await player.node.rest.updatePlayer({
|
|
555
|
-
guildId: state.options.guildId,
|
|
556
|
-
data: {
|
|
557
|
-
voice: {
|
|
558
|
-
token: state.voiceState.event.token,
|
|
559
|
-
endpoint: state.voiceState.event.endpoint,
|
|
560
|
-
sessionId: state.voiceState.sessionId,
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
});
|
|
564
|
-
player.connect();
|
|
565
|
-
const tracks = [];
|
|
566
|
-
const currentTrack = state.queue.current;
|
|
567
|
-
const queueTracks = state.queue.tracks;
|
|
568
|
-
if (state.isAutoplay) {
|
|
569
|
-
const savedUser = state.data.clientUser;
|
|
570
|
-
if (savedUser) {
|
|
571
|
-
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
572
|
-
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
const savedNowPlayingMessage = state.data?.nowPlayingMessage;
|
|
576
|
-
if (savedNowPlayingMessage) {
|
|
577
|
-
player.setNowPlayingMessage(savedNowPlayingMessage);
|
|
578
|
-
}
|
|
579
|
-
if (lavaPlayer.track) {
|
|
580
|
-
await player.queue.clear();
|
|
581
|
-
if (currentTrack) {
|
|
582
|
-
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
583
|
-
}
|
|
584
|
-
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
585
|
-
if (remainingQueue.length > 0) {
|
|
586
|
-
await player.queue.add(remainingQueue);
|
|
587
|
-
}
|
|
588
|
-
player.playing = !lavaPlayer.paused;
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
if (currentTrack) {
|
|
592
|
-
if (queueTracks.length > 0) {
|
|
593
|
-
tracks.push(...queueTracks);
|
|
594
|
-
await player.queue.clear();
|
|
595
|
-
await player.queue.add(tracks);
|
|
596
|
-
}
|
|
597
|
-
await node.trackEnd(player, currentTrack, {
|
|
598
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
599
|
-
type: "TrackEndEvent",
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
603
|
-
const previousQueue = await player.queue.getPrevious();
|
|
604
|
-
const lastTrack = previousQueue?.at(-1);
|
|
605
|
-
if (lastTrack) {
|
|
606
|
-
if (queueTracks.length === 0) {
|
|
607
|
-
await node.trackEnd(player, lastTrack, {
|
|
608
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
609
|
-
type: "TrackEndEvent",
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
tracks.push(...queueTracks);
|
|
614
|
-
if (tracks.length > 0) {
|
|
615
|
-
await player.queue.clear();
|
|
616
|
-
await player.queue.add(tracks);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
else if (queueTracks.length > 0) {
|
|
621
|
-
tracks.push(...queueTracks);
|
|
622
|
-
if (tracks.length > 0) {
|
|
623
|
-
await player.queue.clear();
|
|
624
|
-
await player.queue.add(tracks);
|
|
625
|
-
}
|
|
626
|
-
await node.trackEnd(player, lastTrack, {
|
|
627
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
628
|
-
type: "TrackEndEvent",
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
if (state.queue.previous.length > 0) {
|
|
634
|
-
await player.queue.addPrevious(state.queue.previous);
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
await player.queue.clearPrevious();
|
|
638
|
-
}
|
|
639
|
-
await player.pause(state.paused);
|
|
640
|
-
if (state.trackRepeat)
|
|
641
|
-
player.setTrackRepeat(true);
|
|
642
|
-
if (state.queueRepeat)
|
|
643
|
-
player.setQueueRepeat(true);
|
|
644
|
-
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
645
|
-
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
646
|
-
}
|
|
647
|
-
if (state.data) {
|
|
648
|
-
for (const [name, value] of Object.entries(state.data)) {
|
|
649
|
-
player.set(name, value);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
const filterActions = {
|
|
653
|
-
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
654
|
-
distort: (enabled) => player.filters.distort(enabled),
|
|
655
|
-
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
656
|
-
eightD: (enabled) => player.filters.eightD(enabled),
|
|
657
|
-
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
658
|
-
nightcore: (enabled) => player.filters.nightcore(enabled),
|
|
659
|
-
slowmo: (enabled) => player.filters.slowmo(enabled),
|
|
660
|
-
soft: (enabled) => player.filters.soft(enabled),
|
|
661
|
-
trebleBass: (enabled) => player.filters.trebleBass(enabled),
|
|
662
|
-
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
663
|
-
tv: (enabled) => player.filters.tv(enabled),
|
|
664
|
-
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
665
|
-
vaporwave: (enabled) => player.filters.vaporwave(enabled),
|
|
666
|
-
pop: (enabled) => player.filters.pop(enabled),
|
|
667
|
-
party: (enabled) => player.filters.party(enabled),
|
|
668
|
-
earrape: (enabled) => player.filters.earrape(enabled),
|
|
669
|
-
electronic: (enabled) => player.filters.electronic(enabled),
|
|
670
|
-
radio: (enabled) => player.filters.radio(enabled),
|
|
671
|
-
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
672
|
-
tremolo: (enabled) => player.filters.tremolo(enabled),
|
|
673
|
-
china: (enabled) => player.filters.china(enabled),
|
|
674
|
-
chipmunk: (enabled) => player.filters.chipmunk(enabled),
|
|
675
|
-
darthvader: (enabled) => player.filters.darthvader(enabled),
|
|
676
|
-
daycore: (enabled) => player.filters.daycore(enabled),
|
|
677
|
-
doubletime: (enabled) => player.filters.doubletime(enabled),
|
|
678
|
-
demon: (enabled) => player.filters.demon(enabled),
|
|
679
|
-
};
|
|
680
|
-
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
681
|
-
if (isEnabled && filterActions[filter]) {
|
|
682
|
-
filterActions[filter](true);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
686
|
-
await this.sleep(1000);
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing player state for guild ${guildId}: ${error}`);
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
670
|
+
case Enums_1.StateStorageType.JSON: {
|
|
671
|
+
const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
|
|
672
|
+
try {
|
|
673
|
+
await promises_1.default.access(playersBaseDir).catch(async () => {
|
|
674
|
+
await promises_1.default.mkdir(playersBaseDir, { recursive: true });
|
|
675
|
+
});
|
|
676
|
+
const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
|
|
677
|
+
for (const file of guildDirs) {
|
|
678
|
+
if (!file.isDirectory())
|
|
679
|
+
continue;
|
|
680
|
+
const guildId = file.name;
|
|
681
|
+
const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
|
|
682
|
+
try {
|
|
683
|
+
await promises_1.default.access(stateFilePath);
|
|
684
|
+
const state = JSON.parse(await promises_1.default.readFile(stateFilePath, "utf-8"));
|
|
685
|
+
await this.restorePlayerFromState(node, nodeId, guildId, state, async () => {
|
|
686
|
+
await promises_1.default.rm(stateFilePath, { force: true });
|
|
687
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted state for guild ${guildId}`);
|
|
688
|
+
});
|
|
692
689
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if (!dirent.isDirectory())
|
|
696
|
-
continue;
|
|
697
|
-
const guildId = dirent.name;
|
|
698
|
-
const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
|
|
699
|
-
try {
|
|
700
|
-
await promises_1.default.access(stateFilePath);
|
|
701
|
-
const data = await promises_1.default.readFile(stateFilePath, "utf-8");
|
|
702
|
-
const state = JSON.parse(data);
|
|
703
|
-
if (state && typeof state === "object" && state.node?.options?.identifier === nodeId) {
|
|
704
|
-
await promises_1.default.rm(Utils_1.PlayerUtils.getPlayerStatePath(guildId), { force: true });
|
|
705
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state folder for guild ${guildId}`);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
catch (error) {
|
|
709
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error deleting player state for guild ${guildId}: ${error}`);
|
|
710
|
-
continue;
|
|
711
|
-
}
|
|
690
|
+
catch (error) {
|
|
691
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing guild ${guildId}: ${error}`);
|
|
712
692
|
}
|
|
713
693
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
694
|
+
}
|
|
695
|
+
catch (error) {
|
|
696
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
|
|
717
697
|
}
|
|
718
698
|
break;
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
for (const key of keys) {
|
|
728
|
-
try {
|
|
729
|
-
const data = await this.redis.get(key);
|
|
730
|
-
if (!data)
|
|
731
|
-
continue;
|
|
732
|
-
const state = JSON.parse(data);
|
|
733
|
-
if (!state || typeof state !== "object" || state.clusterId !== this.options.clusterId)
|
|
734
|
-
continue;
|
|
735
|
-
const guildId = key.split(":").pop();
|
|
736
|
-
if (!guildId || state.node.options.identifier !== nodeId)
|
|
737
|
-
continue;
|
|
738
|
-
const lavaPlayer = await node.rest.getPlayer(state.guildId);
|
|
739
|
-
if (!lavaPlayer) {
|
|
740
|
-
await this.destroy(guildId);
|
|
741
|
-
}
|
|
742
|
-
const playerOptions = {
|
|
743
|
-
guildId: state.options.guildId,
|
|
744
|
-
textChannelId: state.options.textChannelId,
|
|
745
|
-
voiceChannelId: state.options.voiceChannelId,
|
|
746
|
-
selfDeafen: state.options.selfDeafen,
|
|
747
|
-
volume: lavaPlayer.volume || state.options.volume,
|
|
748
|
-
nodeIdentifier: nodeId,
|
|
749
|
-
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
750
|
-
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
751
|
-
};
|
|
752
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
|
|
753
|
-
const player = this.create(playerOptions);
|
|
754
|
-
await player.node.rest.updatePlayer({
|
|
755
|
-
guildId: state.options.guildId,
|
|
756
|
-
data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
|
|
757
|
-
});
|
|
758
|
-
player.connect();
|
|
759
|
-
// Rest of the player state restoration code (tracks, filters, etc.)
|
|
760
|
-
const tracks = [];
|
|
761
|
-
const currentTrack = state.queue.current;
|
|
762
|
-
const queueTracks = state.queue.tracks;
|
|
763
|
-
if (state.isAutoplay) {
|
|
764
|
-
const savedUser = state.data.clientUser;
|
|
765
|
-
if (savedUser) {
|
|
766
|
-
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
767
|
-
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
const savedNowPlayingMessage = state.data?.nowPlayingMessage;
|
|
771
|
-
if (savedNowPlayingMessage) {
|
|
772
|
-
player.setNowPlayingMessage(savedNowPlayingMessage);
|
|
773
|
-
}
|
|
774
|
-
if (lavaPlayer.track) {
|
|
775
|
-
await player.queue.clear();
|
|
776
|
-
if (currentTrack) {
|
|
777
|
-
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
778
|
-
}
|
|
779
|
-
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
780
|
-
if (remainingQueue.length > 0) {
|
|
781
|
-
await player.queue.add(remainingQueue);
|
|
782
|
-
}
|
|
783
|
-
player.playing = !lavaPlayer.paused;
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
// LavaPlayer missing track or lavaPlayer is falsy
|
|
787
|
-
if (currentTrack) {
|
|
788
|
-
if (queueTracks.length > 0) {
|
|
789
|
-
tracks.push(...queueTracks);
|
|
790
|
-
await player.queue.clear();
|
|
791
|
-
await player.queue.add(tracks);
|
|
792
|
-
}
|
|
793
|
-
await node.trackEnd(player, currentTrack, {
|
|
794
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
795
|
-
type: "TrackEndEvent",
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
else {
|
|
799
|
-
// No current track, check previous queue for last track
|
|
800
|
-
const previousQueue = await player.queue.getPrevious();
|
|
801
|
-
const lastTrack = previousQueue?.at(-1);
|
|
802
|
-
if (lastTrack) {
|
|
803
|
-
if (queueTracks.length === 0) {
|
|
804
|
-
// If no tracks in queue, end last track
|
|
805
|
-
await node.trackEnd(player, lastTrack, {
|
|
806
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
807
|
-
type: "TrackEndEvent",
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
else {
|
|
811
|
-
// If there are queued tracks, add them
|
|
812
|
-
tracks.push(...queueTracks);
|
|
813
|
-
if (tracks.length > 0) {
|
|
814
|
-
await player.queue.clear();
|
|
815
|
-
await player.queue.add(tracks);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
else {
|
|
820
|
-
if (queueTracks.length > 0) {
|
|
821
|
-
tracks.push(...queueTracks);
|
|
822
|
-
if (tracks.length > 0) {
|
|
823
|
-
await player.queue.clear();
|
|
824
|
-
await player.queue.add(tracks);
|
|
825
|
-
}
|
|
826
|
-
await node.trackEnd(player, lastTrack, {
|
|
827
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
828
|
-
type: "TrackEndEvent",
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (state.queue.previous.length > 0) {
|
|
835
|
-
await player.queue.addPrevious(state.queue.previous);
|
|
836
|
-
}
|
|
837
|
-
else {
|
|
838
|
-
await player.queue.clearPrevious();
|
|
839
|
-
}
|
|
840
|
-
await player.pause(state.paused);
|
|
841
|
-
if (state.trackRepeat)
|
|
842
|
-
player.setTrackRepeat(true);
|
|
843
|
-
if (state.queueRepeat)
|
|
844
|
-
player.setQueueRepeat(true);
|
|
845
|
-
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
846
|
-
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
847
|
-
}
|
|
848
|
-
if (state.data) {
|
|
849
|
-
for (const [name, value] of Object.entries(state.data)) {
|
|
850
|
-
player.set(name, value);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
const filterActions = {
|
|
854
|
-
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
855
|
-
distort: (enabled) => player.filters.distort(enabled),
|
|
856
|
-
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
857
|
-
eightD: (enabled) => player.filters.eightD(enabled),
|
|
858
|
-
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
859
|
-
nightcore: (enabled) => player.filters.nightcore(enabled),
|
|
860
|
-
slowmo: (enabled) => player.filters.slowmo(enabled),
|
|
861
|
-
soft: (enabled) => player.filters.soft(enabled),
|
|
862
|
-
trebleBass: (enabled) => player.filters.trebleBass(enabled),
|
|
863
|
-
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
864
|
-
tv: (enabled) => player.filters.tv(enabled),
|
|
865
|
-
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
866
|
-
vaporwave: (enabled) => player.filters.vaporwave(enabled),
|
|
867
|
-
pop: (enabled) => player.filters.pop(enabled),
|
|
868
|
-
party: (enabled) => player.filters.party(enabled),
|
|
869
|
-
earrape: (enabled) => player.filters.earrape(enabled),
|
|
870
|
-
electronic: (enabled) => player.filters.electronic(enabled),
|
|
871
|
-
radio: (enabled) => player.filters.radio(enabled),
|
|
872
|
-
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
873
|
-
tremolo: (enabled) => player.filters.tremolo(enabled),
|
|
874
|
-
china: (enabled) => player.filters.china(enabled),
|
|
875
|
-
chipmunk: (enabled) => player.filters.chipmunk(enabled),
|
|
876
|
-
darthvader: (enabled) => player.filters.darthvader(enabled),
|
|
877
|
-
daycore: (enabled) => player.filters.daycore(enabled),
|
|
878
|
-
doubletime: (enabled) => player.filters.doubletime(enabled),
|
|
879
|
-
demon: (enabled) => player.filters.demon(enabled),
|
|
880
|
-
};
|
|
881
|
-
// Iterate through filterStatus and apply the enabled filters
|
|
882
|
-
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
883
|
-
if (isEnabled && filterActions[filter]) {
|
|
884
|
-
filterActions[filter](true);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
// After processing, delete the Redis key
|
|
888
|
-
await this.redis.del(key);
|
|
889
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
|
|
890
|
-
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
891
|
-
await this.sleep(1000);
|
|
892
|
-
}
|
|
893
|
-
catch (error) {
|
|
894
|
-
console.log(error);
|
|
895
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
|
|
699
|
+
}
|
|
700
|
+
case Enums_1.StateStorageType.Redis: {
|
|
701
|
+
try {
|
|
702
|
+
const keys = await this.redis.keys(`${Utils_1.PlayerUtils.getRedisKey()}playerstore:*`);
|
|
703
|
+
for (const key of keys) {
|
|
704
|
+
try {
|
|
705
|
+
const data = await this.redis.get(key);
|
|
706
|
+
if (!data)
|
|
896
707
|
continue;
|
|
897
|
-
|
|
708
|
+
const state = JSON.parse(data);
|
|
709
|
+
if (!state || typeof state !== "object")
|
|
710
|
+
continue;
|
|
711
|
+
const guildId = key.split(":").pop();
|
|
712
|
+
if (!guildId)
|
|
713
|
+
continue;
|
|
714
|
+
await this.restorePlayerFromState(node, nodeId, guildId, state, async () => {
|
|
715
|
+
await this.redis.del(key);
|
|
716
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted Redis state: ${key}`);
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
catch (error) {
|
|
720
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
|
|
898
721
|
}
|
|
899
|
-
}
|
|
900
|
-
catch (error) {
|
|
901
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
|
|
902
722
|
}
|
|
903
723
|
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
|
|
726
|
+
}
|
|
904
727
|
break;
|
|
905
|
-
|
|
906
|
-
break;
|
|
728
|
+
}
|
|
907
729
|
}
|
|
908
730
|
this.emit(Enums_1.ManagerEventTypes.Debug, "[MANAGER] Finished loading saved players.");
|
|
909
731
|
this.emit(Enums_1.ManagerEventTypes.RestoreComplete, node);
|
|
@@ -916,11 +738,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
916
738
|
* @returns {Node} The node to use.
|
|
917
739
|
*/
|
|
918
740
|
get useableNode() {
|
|
919
|
-
return this.options.enablePriorityMode
|
|
920
|
-
? this.priorityNode
|
|
921
|
-
: this.options.useNode === Enums_1.UseNodeOptions.LeastLoad
|
|
922
|
-
? this.leastLoadNode.first()
|
|
923
|
-
: this.leastPlayersNode.first();
|
|
741
|
+
return this.options.enablePriorityMode ? this.priorityNode : this.options.useNode === Enums_1.UseNodeOptions.LeastLoad ? this.leastLoadNode.first() : this.leastPlayersNode.first();
|
|
924
742
|
}
|
|
925
743
|
/**
|
|
926
744
|
* Handles the shutdown of the process by saving all active players' states and optionally cleaning up inactive players.
|
|
@@ -950,8 +768,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
950
768
|
}
|
|
951
769
|
});
|
|
952
770
|
await Promise.allSettled(savePromises);
|
|
953
|
-
if (this.options.stateStorage.deleteInactivePlayers)
|
|
954
|
-
await this.cleanupInactivePlayers();
|
|
955
771
|
setTimeout(() => {
|
|
956
772
|
console.warn("\x1b[32m%s\x1b[0m", "MAGMASTREAM INFO: Shutting down complete, exiting...");
|
|
957
773
|
process.exit(0);
|
|
@@ -1079,13 +895,10 @@ class Manager extends events_1.EventEmitter {
|
|
|
1079
895
|
*/
|
|
1080
896
|
async handleVoiceServerUpdate(player, update) {
|
|
1081
897
|
player.voiceState.event = update;
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
});
|
|
1087
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${token} and endpoint ${endpoint} and sessionId ${sessionId}`);
|
|
1088
|
-
return;
|
|
898
|
+
const sessionId = player.voiceState.sessionId;
|
|
899
|
+
const channelId = player.voiceState.channelId;
|
|
900
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice server for player ${player.guildId} with token ${update.token} | endpoint ${update.endpoint} | sessionId ${sessionId} | channelId ${channelId}`);
|
|
901
|
+
await player.updateVoice();
|
|
1089
902
|
}
|
|
1090
903
|
/**
|
|
1091
904
|
* 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.
|
|
@@ -1096,105 +909,23 @@ class Manager extends events_1.EventEmitter {
|
|
|
1096
909
|
*/
|
|
1097
910
|
async handleVoiceStateUpdate(player, update) {
|
|
1098
911
|
this.emit(Enums_1.ManagerEventTypes.Debug, `Updated voice state for player ${player.guildId} with channel id ${update.channel_id} and session id ${update.session_id}`);
|
|
1099
|
-
if (update.channel_id) {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
player.voiceState
|
|
1104
|
-
player.
|
|
1105
|
-
|
|
912
|
+
if (!update.channel_id) {
|
|
913
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerDisconnect, player, player.voiceChannelId);
|
|
914
|
+
player.voiceChannelId = null;
|
|
915
|
+
player.state = Enums_1.StateTypes.Disconnected;
|
|
916
|
+
player.voiceState = Object.assign({});
|
|
917
|
+
if (player.options.pauseOnDisconnect)
|
|
918
|
+
await player.pause(true);
|
|
1106
919
|
return;
|
|
1107
920
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
player.state = Enums_1.StateTypes.Disconnected;
|
|
1111
|
-
player.voiceState = Object.assign({});
|
|
1112
|
-
if (player.options.pauseOnDisconnect) {
|
|
1113
|
-
await player.pause(true);
|
|
1114
|
-
}
|
|
1115
|
-
return;
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Cleans up inactive players by removing their state files from the file system.
|
|
1119
|
-
* This is done to prevent stale state files from accumulating on the file system.
|
|
1120
|
-
*/
|
|
1121
|
-
async cleanupInactivePlayers() {
|
|
1122
|
-
switch (this.options.stateStorage.type) {
|
|
1123
|
-
case Enums_1.StateStorageType.JSON:
|
|
1124
|
-
{
|
|
1125
|
-
const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
|
|
1126
|
-
try {
|
|
1127
|
-
await promises_1.default.mkdir(playersBaseDir, { recursive: true });
|
|
1128
|
-
const activeGuildIds = new Set(this.players.keys());
|
|
1129
|
-
// Cleanup inactive guild directories inside playersBaseDir
|
|
1130
|
-
const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
|
|
1131
|
-
for (const dirent of guildDirs) {
|
|
1132
|
-
if (!dirent.isDirectory())
|
|
1133
|
-
continue;
|
|
1134
|
-
const guildId = dirent.name;
|
|
1135
|
-
if (!activeGuildIds.has(guildId)) {
|
|
1136
|
-
const guildPath = Utils_1.PlayerUtils.getGuildDir(guildId);
|
|
1137
|
-
await promises_1.default.rm(guildPath, { recursive: true, force: true });
|
|
1138
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
catch (err) {
|
|
1143
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error cleaning up inactive JSON players: ${err}`);
|
|
1144
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1145
|
-
? err
|
|
1146
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
1147
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED,
|
|
1148
|
-
message: "Error cleaning up inactive players.",
|
|
1149
|
-
cause: err,
|
|
1150
|
-
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1151
|
-
});
|
|
1152
|
-
console.error(error);
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
break;
|
|
1156
|
-
case Enums_1.StateStorageType.Redis:
|
|
1157
|
-
{
|
|
1158
|
-
const prefix = this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
1159
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
1160
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:";
|
|
1161
|
-
const pattern = `${prefix}queue:*:current`;
|
|
1162
|
-
try {
|
|
1163
|
-
const stream = this.redis.scanStream({
|
|
1164
|
-
match: pattern,
|
|
1165
|
-
count: 100,
|
|
1166
|
-
});
|
|
1167
|
-
for await (const keys of stream) {
|
|
1168
|
-
for (const key of keys) {
|
|
1169
|
-
// Extract guildId from queue key
|
|
1170
|
-
const match = key.match(new RegExp(`^${prefix}queue:(.+):current$`));
|
|
1171
|
-
if (!match)
|
|
1172
|
-
continue;
|
|
1173
|
-
const guildId = match[1];
|
|
1174
|
-
// If player is not active in memory, clean up all keys
|
|
1175
|
-
if (!this.players.has(guildId)) {
|
|
1176
|
-
await this.redis.del(`${prefix}playerstore:${guildId}`, `${prefix}queue:${guildId}:current`, `${prefix}queue:${guildId}:tracks`, `${prefix}queue:${guildId}:previous`);
|
|
1177
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Cleaned inactive Redis player data: ${guildId}`);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
catch (err) {
|
|
1183
|
-
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1184
|
-
? err
|
|
1185
|
-
: new MagmastreamError_1.MagmaStreamError({
|
|
1186
|
-
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
1187
|
-
message: "Error saving player state.",
|
|
1188
|
-
cause: err,
|
|
1189
|
-
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1190
|
-
});
|
|
1191
|
-
console.error(error);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
break;
|
|
1195
|
-
default:
|
|
1196
|
-
break;
|
|
921
|
+
if (player.voiceChannelId !== update.channel_id) {
|
|
922
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerMove, player, player.voiceChannelId, update.channel_id);
|
|
1197
923
|
}
|
|
924
|
+
player.voiceState.sessionId = update.session_id;
|
|
925
|
+
player.voiceState.channelId = update.channel_id;
|
|
926
|
+
player.voiceChannelId = update.channel_id;
|
|
927
|
+
player.options.voiceChannelId = update.channel_id;
|
|
928
|
+
await player.updateVoice();
|
|
1198
929
|
}
|
|
1199
930
|
/**
|
|
1200
931
|
* Cleans up an inactive player by removing its state data.
|
|
@@ -1230,9 +961,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1230
961
|
{
|
|
1231
962
|
try {
|
|
1232
963
|
if (!player) {
|
|
1233
|
-
const prefix =
|
|
1234
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
1235
|
-
: `${this.options.stateStorage.redisConfig.prefix ?? "magmastream"}:`;
|
|
964
|
+
const prefix = Utils_1.PlayerUtils.getRedisKey();
|
|
1236
965
|
const keysToDelete = [
|
|
1237
966
|
`${prefix}playerstore:${guildId}`,
|
|
1238
967
|
`${prefix}queue:${guildId}:tracks`,
|
|
@@ -1340,9 +1069,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1340
1069
|
break;
|
|
1341
1070
|
}
|
|
1342
1071
|
case Enums_1.StateStorageType.Redis: {
|
|
1343
|
-
const prefix =
|
|
1344
|
-
? this.options.stateStorage.redisConfig.prefix
|
|
1345
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:";
|
|
1072
|
+
const prefix = Utils_1.PlayerUtils.getRedisKey();
|
|
1346
1073
|
const patterns = [`${prefix}playerstore:*`, `${prefix}queue:*`];
|
|
1347
1074
|
try {
|
|
1348
1075
|
for (const pattern of patterns) {
|
|
@@ -1441,6 +1168,12 @@ class Manager extends events_1.EventEmitter {
|
|
|
1441
1168
|
}
|
|
1442
1169
|
return this._send(packet);
|
|
1443
1170
|
}
|
|
1171
|
+
getUserFromCache(id) {
|
|
1172
|
+
return this._getUser?.(id);
|
|
1173
|
+
}
|
|
1174
|
+
getGuildFromCache(id) {
|
|
1175
|
+
return this._getGuild?.(id);
|
|
1176
|
+
}
|
|
1444
1177
|
sendPacket(packet) {
|
|
1445
1178
|
return this.send(packet);
|
|
1446
1179
|
}
|
|
@@ -1455,5 +1188,14 @@ class Manager extends events_1.EventEmitter {
|
|
|
1455
1188
|
return { id: user }; // fallback by ID only
|
|
1456
1189
|
return user; // default: just return the portable user
|
|
1457
1190
|
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Resolves a Guild ID to a real guild object.
|
|
1193
|
+
* Can be overridden by wrapper managers to return wrapper-specific Guild classes.
|
|
1194
|
+
*/
|
|
1195
|
+
resolveGuild(guildId) {
|
|
1196
|
+
if (!guildId)
|
|
1197
|
+
return null;
|
|
1198
|
+
return this._getGuild?.(guildId);
|
|
1199
|
+
}
|
|
1458
1200
|
}
|
|
1459
1201
|
exports.Manager = Manager;
|