magmastream 2.9.2-dev.1 → 2.9.2-dev.2
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 +63 -4
- package/dist/index.js +1 -0
- package/dist/statestorage/JsonQueue.js +273 -171
- package/dist/statestorage/MemoryQueue.js +260 -203
- package/dist/statestorage/RedisQueue.js +396 -203
- package/dist/structures/Enums.js +110 -1
- package/dist/structures/Filters.js +27 -13
- package/dist/structures/MagmastreamError.js +19 -0
- package/dist/structures/Manager.js +345 -219
- package/dist/structures/Node.js +222 -64
- package/dist/structures/Player.js +169 -56
- package/dist/structures/Rest.js +23 -12
- package/dist/structures/Utils.js +66 -65
- package/dist/utils/managerCheck.js +99 -21
- package/dist/utils/nodeCheck.js +59 -34
- package/dist/utils/playerCheck.js +47 -28
- package/package.json +1 -1
|
@@ -14,6 +14,7 @@ const path_1 = tslib_1.__importDefault(require("path"));
|
|
|
14
14
|
const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
|
|
15
15
|
const Enums_1 = require("./Enums");
|
|
16
16
|
const package_json_1 = require("../../package.json");
|
|
17
|
+
const MagmastreamError_1 = require("./MagmastreamError");
|
|
17
18
|
/**
|
|
18
19
|
* The main hub for interacting with Lavalink and using Magmastream.
|
|
19
20
|
*/
|
|
@@ -105,8 +106,16 @@ class Manager extends events_1.EventEmitter {
|
|
|
105
106
|
process.exit(0);
|
|
106
107
|
}, 2000);
|
|
107
108
|
}
|
|
108
|
-
catch (
|
|
109
|
-
|
|
109
|
+
catch (err) {
|
|
110
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
111
|
+
? err
|
|
112
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
113
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
114
|
+
message: "An unknown error occurred.",
|
|
115
|
+
cause: err,
|
|
116
|
+
context: { stage: "SIGINT" },
|
|
117
|
+
});
|
|
118
|
+
console.error(error);
|
|
110
119
|
process.exit(1);
|
|
111
120
|
}
|
|
112
121
|
});
|
|
@@ -117,8 +126,16 @@ class Manager extends events_1.EventEmitter {
|
|
|
117
126
|
console.warn("\x1b[32mShutdown complete. Exiting now...\x1b[0m");
|
|
118
127
|
process.exit(0);
|
|
119
128
|
}
|
|
120
|
-
catch (
|
|
121
|
-
|
|
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);
|
|
122
139
|
process.exit(1);
|
|
123
140
|
}
|
|
124
141
|
});
|
|
@@ -136,7 +153,11 @@ class Manager extends events_1.EventEmitter {
|
|
|
136
153
|
const { clientId, clusterId = 0 } = options;
|
|
137
154
|
if (clientId !== undefined) {
|
|
138
155
|
if (typeof clientId !== "string" || !/^\d+$/.test(clientId)) {
|
|
139
|
-
throw new
|
|
156
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
157
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_INIT_FAILED,
|
|
158
|
+
message: '"clientId" must be a valid Discord client ID.',
|
|
159
|
+
context: { clientId },
|
|
160
|
+
});
|
|
140
161
|
}
|
|
141
162
|
this.options.clientId = clientId;
|
|
142
163
|
}
|
|
@@ -161,7 +182,15 @@ class Manager extends events_1.EventEmitter {
|
|
|
161
182
|
await node.connect();
|
|
162
183
|
}
|
|
163
184
|
catch (err) {
|
|
164
|
-
|
|
185
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
186
|
+
? err
|
|
187
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
188
|
+
code: Enums_1.MagmaStreamErrorCode.NODE_CONNECT_FAILED,
|
|
189
|
+
message: `Failed to connect node "${node.options.identifier}".`,
|
|
190
|
+
cause: err instanceof Error ? err : undefined,
|
|
191
|
+
context: { nodeId: node.options.identifier },
|
|
192
|
+
});
|
|
193
|
+
this.emit(Enums_1.ManagerEventTypes.NodeError, node, error);
|
|
165
194
|
}
|
|
166
195
|
}
|
|
167
196
|
this.loadPlugins();
|
|
@@ -176,8 +205,13 @@ class Manager extends events_1.EventEmitter {
|
|
|
176
205
|
*/
|
|
177
206
|
async search(query, requester) {
|
|
178
207
|
const node = this.useableNode;
|
|
179
|
-
if (!node)
|
|
180
|
-
throw new
|
|
208
|
+
if (!node) {
|
|
209
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
210
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_NO_NODES,
|
|
211
|
+
message: "No available nodes to perform the search.",
|
|
212
|
+
context: { query, requester },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
181
215
|
const _query = typeof query === "string" ? { query } : query;
|
|
182
216
|
const _source = _query.source ?? this.options.defaultSearchPlatform;
|
|
183
217
|
const isUrl = /^https?:\/\//.test(_query.query);
|
|
@@ -185,8 +219,13 @@ class Manager extends events_1.EventEmitter {
|
|
|
185
219
|
this.emit(Enums_1.ManagerEventTypes.Debug, isUrl ? `[MANAGER] Performing search for: ${_query.query}` : `[MANAGER] Performing ${_source} search for: ${_query.query}`);
|
|
186
220
|
try {
|
|
187
221
|
const res = (await node.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(search)}`));
|
|
188
|
-
if (!res)
|
|
189
|
-
throw new
|
|
222
|
+
if (!res) {
|
|
223
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
224
|
+
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
225
|
+
message: `No results returned from Lavalink for query "${search}".`,
|
|
226
|
+
context: { query: search, requester },
|
|
227
|
+
});
|
|
228
|
+
}
|
|
190
229
|
let result;
|
|
191
230
|
switch (res.loadType) {
|
|
192
231
|
case Enums_1.LoadTypes.Search: {
|
|
@@ -245,7 +284,14 @@ class Manager extends events_1.EventEmitter {
|
|
|
245
284
|
return result;
|
|
246
285
|
}
|
|
247
286
|
catch (err) {
|
|
248
|
-
throw
|
|
287
|
+
throw err instanceof MagmastreamError_1.MagmaStreamError
|
|
288
|
+
? err
|
|
289
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
290
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SEARCH_FAILED,
|
|
291
|
+
message: `An error occurred while searching: ${err instanceof Error ? err.message : String(err)}`,
|
|
292
|
+
cause: err instanceof Error ? err : undefined,
|
|
293
|
+
context: { query, requester },
|
|
294
|
+
});
|
|
249
295
|
}
|
|
250
296
|
}
|
|
251
297
|
/**
|
|
@@ -311,7 +357,11 @@ class Manager extends events_1.EventEmitter {
|
|
|
311
357
|
const node = this.nodes.get(identifier);
|
|
312
358
|
if (!node) {
|
|
313
359
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Tried to destroy non-existent node: ${identifier}`);
|
|
314
|
-
|
|
360
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
361
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_NODE_NOT_FOUND,
|
|
362
|
+
message: "Node not found.",
|
|
363
|
+
context: { identifier },
|
|
364
|
+
});
|
|
315
365
|
}
|
|
316
366
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Destroying node: ${identifier}`);
|
|
317
367
|
this.nodes.delete(identifier);
|
|
@@ -359,12 +409,19 @@ class Manager extends events_1.EventEmitter {
|
|
|
359
409
|
async decodeTracks(tracks) {
|
|
360
410
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Decoding tracks: ${Utils_1.JSONUtils.safe(tracks, 2)}`);
|
|
361
411
|
return new Promise(async (resolve, reject) => {
|
|
362
|
-
const node = this.
|
|
363
|
-
if (!node)
|
|
364
|
-
throw new
|
|
412
|
+
const node = this.useableNode;
|
|
413
|
+
if (!node) {
|
|
414
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
415
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_NO_NODES,
|
|
416
|
+
message: "No available nodes to decode tracks.",
|
|
417
|
+
});
|
|
418
|
+
}
|
|
365
419
|
const res = (await node.rest.post("/v4/decodetracks", Utils_1.JSONUtils.safe(tracks, 2)).catch((err) => reject(err)));
|
|
366
420
|
if (!res) {
|
|
367
|
-
|
|
421
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
422
|
+
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
423
|
+
message: "No decoded tracks returned from node.",
|
|
424
|
+
});
|
|
368
425
|
}
|
|
369
426
|
return resolve(res);
|
|
370
427
|
});
|
|
@@ -443,9 +500,14 @@ class Manager extends events_1.EventEmitter {
|
|
|
443
500
|
async loadPlayerStates(nodeId) {
|
|
444
501
|
this.emit(Enums_1.ManagerEventTypes.Debug, "[MANAGER] Loading saved players.");
|
|
445
502
|
const node = this.nodes.get(nodeId);
|
|
446
|
-
if (!node)
|
|
447
|
-
|
|
448
|
-
|
|
503
|
+
if (!node) {
|
|
504
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Tried to load non-existent node: ${nodeId}`);
|
|
505
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
506
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_NODE_NOT_FOUND,
|
|
507
|
+
message: "Node not found.",
|
|
508
|
+
context: { nodeId },
|
|
509
|
+
});
|
|
510
|
+
}
|
|
449
511
|
switch (this.options.stateStorage.type) {
|
|
450
512
|
case Enums_1.StateStorageType.Memory:
|
|
451
513
|
case Enums_1.StateStorageType.JSON:
|
|
@@ -472,7 +534,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
472
534
|
continue;
|
|
473
535
|
if (!state.guildId || state.node?.options?.identifier !== nodeId)
|
|
474
536
|
continue;
|
|
475
|
-
const lavaPlayer =
|
|
537
|
+
const lavaPlayer = await node.rest.getPlayer(state.guildId);
|
|
476
538
|
if (!lavaPlayer) {
|
|
477
539
|
await this.destroy(state.guildId);
|
|
478
540
|
continue;
|
|
@@ -509,15 +571,16 @@ class Manager extends events_1.EventEmitter {
|
|
|
509
571
|
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
510
572
|
}
|
|
511
573
|
}
|
|
512
|
-
if (lavaPlayer
|
|
513
|
-
|
|
514
|
-
if (currentTrack
|
|
515
|
-
await player.queue.
|
|
574
|
+
if (lavaPlayer.track) {
|
|
575
|
+
await player.queue.clear();
|
|
576
|
+
if (currentTrack) {
|
|
577
|
+
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
|
|
516
578
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
await player.queue.add(
|
|
579
|
+
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
580
|
+
if (remainingQueue.length > 0) {
|
|
581
|
+
await player.queue.add(remainingQueue);
|
|
520
582
|
}
|
|
583
|
+
player.playing = !lavaPlayer.paused;
|
|
521
584
|
}
|
|
522
585
|
else {
|
|
523
586
|
if (currentTrack) {
|
|
@@ -568,12 +631,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
568
631
|
else {
|
|
569
632
|
await player.queue.clearPrevious();
|
|
570
633
|
}
|
|
571
|
-
|
|
572
|
-
await player.pause(true);
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
player.paused = false;
|
|
576
|
-
}
|
|
634
|
+
await player.pause(state.paused);
|
|
577
635
|
if (state.trackRepeat)
|
|
578
636
|
player.setTrackRepeat(true);
|
|
579
637
|
if (state.queueRepeat)
|
|
@@ -670,166 +728,159 @@ class Manager extends events_1.EventEmitter {
|
|
|
670
728
|
if (!state || typeof state !== "object" || state.clusterId !== this.options.clusterId)
|
|
671
729
|
continue;
|
|
672
730
|
const guildId = key.split(":").pop();
|
|
673
|
-
if (!guildId)
|
|
731
|
+
if (!guildId || state.node.options.identifier !== nodeId)
|
|
674
732
|
continue;
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
733
|
+
const lavaPlayer = await node.rest.getPlayer(state.guildId);
|
|
734
|
+
if (!lavaPlayer) {
|
|
735
|
+
await this.destroy(guildId);
|
|
736
|
+
}
|
|
737
|
+
const playerOptions = {
|
|
738
|
+
guildId: state.options.guildId,
|
|
739
|
+
textChannelId: state.options.textChannelId,
|
|
740
|
+
voiceChannelId: state.options.voiceChannelId,
|
|
741
|
+
selfDeafen: state.options.selfDeafen,
|
|
742
|
+
volume: lavaPlayer.volume || state.options.volume,
|
|
743
|
+
nodeIdentifier: nodeId,
|
|
744
|
+
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
745
|
+
};
|
|
746
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
|
|
747
|
+
const player = this.create(playerOptions);
|
|
748
|
+
await player.node.rest.updatePlayer({
|
|
749
|
+
guildId: state.options.guildId,
|
|
750
|
+
data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
|
|
751
|
+
});
|
|
752
|
+
if (!lavaPlayer || !lavaPlayer.state.connected) {
|
|
695
753
|
player.connect();
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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;
|
|
759
|
+
if (state.isAutoplay) {
|
|
760
|
+
const savedUser = state.data.clientUser;
|
|
761
|
+
if (savedUser) {
|
|
762
|
+
const autoPlayUser = await player.manager.resolveUser(savedUser);
|
|
763
|
+
player.setAutoplay(true, autoPlayUser, state.autoplayTries);
|
|
706
764
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
765
|
+
}
|
|
766
|
+
if (lavaPlayer.track) {
|
|
767
|
+
await player.queue.clear();
|
|
768
|
+
if (currentTrack) {
|
|
769
|
+
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
|
|
770
|
+
}
|
|
771
|
+
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
772
|
+
if (remainingQueue.length > 0) {
|
|
773
|
+
await player.queue.add(remainingQueue);
|
|
774
|
+
}
|
|
775
|
+
player.playing = !lavaPlayer.paused;
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
// LavaPlayer missing track or lavaPlayer is falsy
|
|
779
|
+
if (currentTrack) {
|
|
780
|
+
if (queueTracks.length > 0) {
|
|
781
|
+
tracks.push(...queueTracks);
|
|
716
782
|
await player.queue.clear();
|
|
717
783
|
await player.queue.add(tracks);
|
|
718
784
|
}
|
|
785
|
+
await node.trackEnd(player, currentTrack, {
|
|
786
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
787
|
+
type: "TrackEndEvent",
|
|
788
|
+
});
|
|
719
789
|
}
|
|
720
790
|
else {
|
|
721
|
-
//
|
|
722
|
-
|
|
723
|
-
|
|
791
|
+
// No current track, check previous queue for last track
|
|
792
|
+
const previousQueue = await player.queue.getPrevious();
|
|
793
|
+
const lastTrack = previousQueue?.at(-1);
|
|
794
|
+
if (lastTrack) {
|
|
795
|
+
if (queueTracks.length === 0) {
|
|
796
|
+
// If no tracks in queue, end last track
|
|
797
|
+
await node.trackEnd(player, lastTrack, {
|
|
798
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
799
|
+
type: "TrackEndEvent",
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
// If there are queued tracks, add them
|
|
724
804
|
tracks.push(...queueTracks);
|
|
725
|
-
|
|
726
|
-
|
|
805
|
+
if (tracks.length > 0) {
|
|
806
|
+
await player.queue.clear();
|
|
807
|
+
await player.queue.add(tracks);
|
|
808
|
+
}
|
|
727
809
|
}
|
|
728
|
-
await node.trackEnd(player, currentTrack, {
|
|
729
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
730
|
-
type: "TrackEndEvent",
|
|
731
|
-
});
|
|
732
810
|
}
|
|
733
811
|
else {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
// If no tracks in queue, end last track
|
|
740
|
-
await node.trackEnd(player, lastTrack, {
|
|
741
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
742
|
-
type: "TrackEndEvent",
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
// If there are queued tracks, add them
|
|
747
|
-
tracks.push(...queueTracks);
|
|
748
|
-
if (tracks.length > 0) {
|
|
749
|
-
await player.queue.clear();
|
|
750
|
-
await player.queue.add(tracks);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
if (queueTracks.length > 0) {
|
|
756
|
-
tracks.push(...queueTracks);
|
|
757
|
-
if (tracks.length > 0) {
|
|
758
|
-
await player.queue.clear();
|
|
759
|
-
await player.queue.add(tracks);
|
|
760
|
-
}
|
|
761
|
-
await node.trackEnd(player, lastTrack, {
|
|
762
|
-
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
763
|
-
type: "TrackEndEvent",
|
|
764
|
-
});
|
|
812
|
+
if (queueTracks.length > 0) {
|
|
813
|
+
tracks.push(...queueTracks);
|
|
814
|
+
if (tracks.length > 0) {
|
|
815
|
+
await player.queue.clear();
|
|
816
|
+
await player.queue.add(tracks);
|
|
765
817
|
}
|
|
818
|
+
await node.trackEnd(player, lastTrack, {
|
|
819
|
+
reason: Enums_1.TrackEndReasonTypes.Finished,
|
|
820
|
+
type: "TrackEndEvent",
|
|
821
|
+
});
|
|
766
822
|
}
|
|
767
823
|
}
|
|
768
824
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
player.
|
|
787
|
-
}
|
|
788
|
-
if (state.data) {
|
|
789
|
-
for (const [name, value] of Object.entries(state.data)) {
|
|
790
|
-
player.set(name, value);
|
|
791
|
-
}
|
|
825
|
+
}
|
|
826
|
+
if (state.queue.previous.length > 0) {
|
|
827
|
+
await player.queue.addPrevious(state.queue.previous);
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
await player.queue.clearPrevious();
|
|
831
|
+
}
|
|
832
|
+
await player.pause(state.paused);
|
|
833
|
+
if (state.trackRepeat)
|
|
834
|
+
player.setTrackRepeat(true);
|
|
835
|
+
if (state.queueRepeat)
|
|
836
|
+
player.setQueueRepeat(true);
|
|
837
|
+
if (state.dynamicRepeat && state.dynamicLoopInterval) {
|
|
838
|
+
player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval);
|
|
839
|
+
}
|
|
840
|
+
if (state.data) {
|
|
841
|
+
for (const [name, value] of Object.entries(state.data)) {
|
|
842
|
+
player.set(name, value);
|
|
792
843
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
844
|
+
}
|
|
845
|
+
const filterActions = {
|
|
846
|
+
bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
|
|
847
|
+
distort: (enabled) => player.filters.distort(enabled),
|
|
848
|
+
setDistortion: () => player.filters.setDistortion(state.filters.distortion),
|
|
849
|
+
eightD: (enabled) => player.filters.eightD(enabled),
|
|
850
|
+
setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
|
|
851
|
+
nightcore: (enabled) => player.filters.nightcore(enabled),
|
|
852
|
+
slowmo: (enabled) => player.filters.slowmo(enabled),
|
|
853
|
+
soft: (enabled) => player.filters.soft(enabled),
|
|
854
|
+
trebleBass: (enabled) => player.filters.trebleBass(enabled),
|
|
855
|
+
setTimescale: () => player.filters.setTimescale(state.filters.timescale),
|
|
856
|
+
tv: (enabled) => player.filters.tv(enabled),
|
|
857
|
+
vibrato: () => player.filters.setVibrato(state.filters.vibrato),
|
|
858
|
+
vaporwave: (enabled) => player.filters.vaporwave(enabled),
|
|
859
|
+
pop: (enabled) => player.filters.pop(enabled),
|
|
860
|
+
party: (enabled) => player.filters.party(enabled),
|
|
861
|
+
earrape: (enabled) => player.filters.earrape(enabled),
|
|
862
|
+
electronic: (enabled) => player.filters.electronic(enabled),
|
|
863
|
+
radio: (enabled) => player.filters.radio(enabled),
|
|
864
|
+
setRotation: () => player.filters.setRotation(state.filters.rotation),
|
|
865
|
+
tremolo: (enabled) => player.filters.tremolo(enabled),
|
|
866
|
+
china: (enabled) => player.filters.china(enabled),
|
|
867
|
+
chipmunk: (enabled) => player.filters.chipmunk(enabled),
|
|
868
|
+
darthvader: (enabled) => player.filters.darthvader(enabled),
|
|
869
|
+
daycore: (enabled) => player.filters.daycore(enabled),
|
|
870
|
+
doubletime: (enabled) => player.filters.doubletime(enabled),
|
|
871
|
+
demon: (enabled) => player.filters.demon(enabled),
|
|
872
|
+
};
|
|
873
|
+
// Iterate through filterStatus and apply the enabled filters
|
|
874
|
+
for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
|
|
875
|
+
if (isEnabled && filterActions[filter]) {
|
|
876
|
+
filterActions[filter](true);
|
|
826
877
|
}
|
|
827
|
-
// After processing, delete the Redis key
|
|
828
|
-
await this.redis.del(key);
|
|
829
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
|
|
830
|
-
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
831
|
-
await this.sleep(1000);
|
|
832
878
|
}
|
|
879
|
+
// After processing, delete the Redis key
|
|
880
|
+
await this.redis.del(key);
|
|
881
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
|
|
882
|
+
this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
|
|
883
|
+
await this.sleep(1000);
|
|
833
884
|
}
|
|
834
885
|
catch (error) {
|
|
835
886
|
console.log(error);
|
|
@@ -839,7 +890,6 @@ class Manager extends events_1.EventEmitter {
|
|
|
839
890
|
}
|
|
840
891
|
}
|
|
841
892
|
catch (error) {
|
|
842
|
-
console.log(error);
|
|
843
893
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
|
|
844
894
|
}
|
|
845
895
|
}
|
|
@@ -879,8 +929,16 @@ class Manager extends events_1.EventEmitter {
|
|
|
879
929
|
try {
|
|
880
930
|
await this.savePlayerState(guildId);
|
|
881
931
|
}
|
|
882
|
-
catch (
|
|
883
|
-
|
|
932
|
+
catch (err) {
|
|
933
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
934
|
+
? err
|
|
935
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
936
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
937
|
+
message: "Error saving player state.",
|
|
938
|
+
cause: err,
|
|
939
|
+
context: { guildId },
|
|
940
|
+
});
|
|
941
|
+
console.error(error);
|
|
884
942
|
}
|
|
885
943
|
});
|
|
886
944
|
await Promise.allSettled(savePromises);
|
|
@@ -891,8 +949,16 @@ class Manager extends events_1.EventEmitter {
|
|
|
891
949
|
process.exit(0);
|
|
892
950
|
}, 500);
|
|
893
951
|
}
|
|
894
|
-
catch (
|
|
895
|
-
|
|
952
|
+
catch (err) {
|
|
953
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
954
|
+
? err
|
|
955
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
956
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
957
|
+
message: "Error saving player state.",
|
|
958
|
+
cause: err,
|
|
959
|
+
context: { stage: "SHUTDOWN" },
|
|
960
|
+
});
|
|
961
|
+
console.error(error);
|
|
896
962
|
process.exit(1);
|
|
897
963
|
}
|
|
898
964
|
}
|
|
@@ -1062,10 +1128,18 @@ class Manager extends events_1.EventEmitter {
|
|
|
1062
1128
|
}
|
|
1063
1129
|
}
|
|
1064
1130
|
}
|
|
1065
|
-
catch (
|
|
1066
|
-
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error cleaning up inactive JSON players: ${
|
|
1131
|
+
catch (err) {
|
|
1132
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error cleaning up inactive JSON players: ${err}`);
|
|
1133
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1134
|
+
? err
|
|
1135
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
1136
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED,
|
|
1137
|
+
message: "Error cleaning up inactive players.",
|
|
1138
|
+
cause: err,
|
|
1139
|
+
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1140
|
+
});
|
|
1141
|
+
console.error(error);
|
|
1067
1142
|
}
|
|
1068
|
-
return;
|
|
1069
1143
|
}
|
|
1070
1144
|
break;
|
|
1071
1145
|
case Enums_1.StateStorageType.Redis:
|
|
@@ -1074,25 +1148,37 @@ class Manager extends events_1.EventEmitter {
|
|
|
1074
1148
|
? this.options.stateStorage.redisConfig.prefix
|
|
1075
1149
|
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:";
|
|
1076
1150
|
const pattern = `${prefix}queue:*:current`;
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
for (const
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1151
|
+
try {
|
|
1152
|
+
const stream = this.redis.scanStream({
|
|
1153
|
+
match: pattern,
|
|
1154
|
+
count: 100,
|
|
1155
|
+
});
|
|
1156
|
+
for await (const keys of stream) {
|
|
1157
|
+
for (const key of keys) {
|
|
1158
|
+
// Extract guildId from queue key
|
|
1159
|
+
const match = key.match(new RegExp(`^${prefix}queue:(.+):current$`));
|
|
1160
|
+
if (!match)
|
|
1161
|
+
continue;
|
|
1162
|
+
const guildId = match[1];
|
|
1163
|
+
// If player is not active in memory, clean up all keys
|
|
1164
|
+
if (!this.players.has(guildId)) {
|
|
1165
|
+
await this.redis.del(`${prefix}playerstore:${guildId}`, `${prefix}queue:${guildId}:current`, `${prefix}queue:${guildId}:tracks`, `${prefix}queue:${guildId}:previous`);
|
|
1166
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Cleaned inactive Redis player data: ${guildId}`);
|
|
1167
|
+
}
|
|
1092
1168
|
}
|
|
1093
1169
|
}
|
|
1094
1170
|
}
|
|
1095
|
-
|
|
1171
|
+
catch (err) {
|
|
1172
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1173
|
+
? err
|
|
1174
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
1175
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_SHUTDOWN_FAILED,
|
|
1176
|
+
message: "Error saving player state.",
|
|
1177
|
+
cause: err,
|
|
1178
|
+
context: { stage: "CLEANUP_INACTIVE_PLAYERS" },
|
|
1179
|
+
});
|
|
1180
|
+
console.error(error);
|
|
1181
|
+
}
|
|
1096
1182
|
}
|
|
1097
1183
|
break;
|
|
1098
1184
|
default:
|
|
@@ -1105,38 +1191,57 @@ class Manager extends events_1.EventEmitter {
|
|
|
1105
1191
|
* @param guildId The guild ID of the player to clean up.
|
|
1106
1192
|
*/
|
|
1107
1193
|
async cleanupInactivePlayer(guildId) {
|
|
1194
|
+
const player = this.getPlayer(guildId);
|
|
1108
1195
|
switch (this.options.stateStorage.type) {
|
|
1109
1196
|
case Enums_1.StateStorageType.JSON:
|
|
1110
1197
|
{
|
|
1111
1198
|
try {
|
|
1112
|
-
if (!
|
|
1199
|
+
if (!player) {
|
|
1113
1200
|
const guildDir = Utils_1.PlayerUtils.getGuildDir(guildId);
|
|
1114
1201
|
await promises_1.default.rm(guildDir, { recursive: true, force: true });
|
|
1115
1202
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
|
|
1116
1203
|
}
|
|
1117
1204
|
}
|
|
1118
|
-
catch (
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1205
|
+
catch (err) {
|
|
1206
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1207
|
+
? err
|
|
1208
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
1209
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED,
|
|
1210
|
+
message: "Error cleaning up inactive player.",
|
|
1211
|
+
cause: err,
|
|
1212
|
+
context: { guildId },
|
|
1213
|
+
});
|
|
1214
|
+
console.error(error);
|
|
1122
1215
|
}
|
|
1123
1216
|
}
|
|
1124
1217
|
break;
|
|
1125
1218
|
case Enums_1.StateStorageType.Redis:
|
|
1126
1219
|
{
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1220
|
+
try {
|
|
1221
|
+
if (!player) {
|
|
1222
|
+
const prefix = this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
1223
|
+
? this.options.stateStorage.redisConfig.prefix
|
|
1224
|
+
: `${this.options.stateStorage.redisConfig.prefix ?? "magmastream"}:`;
|
|
1225
|
+
const keysToDelete = [
|
|
1226
|
+
`${prefix}playerstore:${guildId}`,
|
|
1227
|
+
`${prefix}queue:${guildId}:tracks`,
|
|
1228
|
+
`${prefix}queue:${guildId}:current`,
|
|
1229
|
+
`${prefix}queue:${guildId}:previous`,
|
|
1230
|
+
];
|
|
1231
|
+
await this.redis.del(...keysToDelete);
|
|
1232
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted Redis player and queue data for: ${guildId}`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
catch (err) {
|
|
1236
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1237
|
+
? err
|
|
1238
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
1239
|
+
code: Enums_1.MagmaStreamErrorCode.MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED,
|
|
1240
|
+
message: "Error cleaning up inactive player.",
|
|
1241
|
+
cause: err,
|
|
1242
|
+
context: { guildId },
|
|
1243
|
+
});
|
|
1244
|
+
console.error(error);
|
|
1140
1245
|
}
|
|
1141
1246
|
}
|
|
1142
1247
|
break;
|
|
@@ -1151,8 +1256,13 @@ class Manager extends events_1.EventEmitter {
|
|
|
1151
1256
|
if (!Array.isArray(this.options.enabledPlugins))
|
|
1152
1257
|
return;
|
|
1153
1258
|
for (const [index, plugin] of this.options.enabledPlugins.entries()) {
|
|
1259
|
+
// Validate plugin class
|
|
1154
1260
|
if (!(plugin instanceof __1.Plugin)) {
|
|
1155
|
-
throw new
|
|
1261
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
1262
|
+
code: Enums_1.MagmaStreamErrorCode.PLUGIN_LOAD_FAILED,
|
|
1263
|
+
message: `Plugin at index ${index} does not extend Plugin.`,
|
|
1264
|
+
context: { index, plugin },
|
|
1265
|
+
});
|
|
1156
1266
|
}
|
|
1157
1267
|
try {
|
|
1158
1268
|
plugin.load(this);
|
|
@@ -1160,7 +1270,15 @@ class Manager extends events_1.EventEmitter {
|
|
|
1160
1270
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[PLUGIN] Loaded plugin: ${plugin.name}`);
|
|
1161
1271
|
}
|
|
1162
1272
|
catch (err) {
|
|
1163
|
-
|
|
1273
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1274
|
+
? err
|
|
1275
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
1276
|
+
code: Enums_1.MagmaStreamErrorCode.PLUGIN_RUNTIME_ERROR,
|
|
1277
|
+
message: `Failed to load plugin "${plugin.name}".`,
|
|
1278
|
+
cause: err instanceof Error ? err : undefined,
|
|
1279
|
+
context: { pluginName: plugin.name, index },
|
|
1280
|
+
});
|
|
1281
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[PLUGIN] ${error.name}: ${error.message}`);
|
|
1164
1282
|
}
|
|
1165
1283
|
}
|
|
1166
1284
|
}
|
|
@@ -1174,7 +1292,15 @@ class Manager extends events_1.EventEmitter {
|
|
|
1174
1292
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[PLUGIN] Unloaded plugin: ${plugin.name}`);
|
|
1175
1293
|
}
|
|
1176
1294
|
catch (err) {
|
|
1177
|
-
|
|
1295
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
1296
|
+
? err
|
|
1297
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
1298
|
+
code: Enums_1.MagmaStreamErrorCode.PLUGIN_RUNTIME_ERROR,
|
|
1299
|
+
message: `Failed to unload plugin "${plugin.name}".`,
|
|
1300
|
+
cause: err instanceof Error ? err : undefined,
|
|
1301
|
+
context: { pluginName: plugin.name },
|
|
1302
|
+
});
|
|
1303
|
+
this.emit(Enums_1.ManagerEventTypes.Debug, `[PLUGIN] ${error.name}: ${error.message}`);
|
|
1178
1304
|
}
|
|
1179
1305
|
}
|
|
1180
1306
|
this.loadedPlugins.clear();
|