magmastream 2.9.3-dev.0 → 2.9.3-dev.10
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 +21 -5
- package/dist/statestorage/JsonQueue.js +1 -2
- package/dist/statestorage/MemoryQueue.js +1 -2
- package/dist/statestorage/RedisQueue.js +1 -2
- package/dist/structures/Enums.js +2 -0
- package/dist/structures/Manager.js +20 -19
- package/dist/structures/Node.js +4 -5
- package/dist/structures/Player.js +9 -8
- package/dist/structures/Rest.js +41 -21
- package/dist/structures/Utils.js +104 -79
- package/package.json +25 -21
package/dist/index.d.ts
CHANGED
|
@@ -170,7 +170,9 @@ declare enum TrackPartial {
|
|
|
170
170
|
/** The plugin info of the track */
|
|
171
171
|
PluginInfo = "pluginInfo",
|
|
172
172
|
/** The custom data of the track */
|
|
173
|
-
CustomData = "customData"
|
|
173
|
+
CustomData = "customData",
|
|
174
|
+
/** Whether the track got autoplayed */
|
|
175
|
+
IsAutoPlay = "isAutoplay"
|
|
174
176
|
}
|
|
175
177
|
/**
|
|
176
178
|
* Manager Event Types Enum
|
|
@@ -1189,6 +1191,8 @@ interface Track {
|
|
|
1189
1191
|
pluginInfo: TrackPluginInfo;
|
|
1190
1192
|
/** Add your own data to the track. */
|
|
1191
1193
|
customData: Record<string, unknown>;
|
|
1194
|
+
/** If the track got added by autoplay. */
|
|
1195
|
+
readonly isAutoplay: boolean;
|
|
1192
1196
|
}
|
|
1193
1197
|
/**
|
|
1194
1198
|
* Track Plugin Info
|
|
@@ -3462,18 +3466,30 @@ declare abstract class TrackUtils {
|
|
|
3462
3466
|
static setTrackPartial(partial: TrackPartial[]): void;
|
|
3463
3467
|
/**
|
|
3464
3468
|
* Checks if the provided argument is a valid Track.
|
|
3465
|
-
*
|
|
3466
|
-
* @param trackOrTracks The Track or array of Tracks to check.
|
|
3469
|
+
* @param value The value to check.
|
|
3467
3470
|
* @returns {boolean} Whether the provided argument is a valid Track.
|
|
3468
3471
|
*/
|
|
3469
|
-
static
|
|
3472
|
+
static isTrack(track: unknown): track is Track;
|
|
3473
|
+
/**
|
|
3474
|
+
* Checks if the provided argument is a valid Track array.
|
|
3475
|
+
* @param value The value to check.
|
|
3476
|
+
* @returns {boolean} Whether the provided argument is a valid Track array.
|
|
3477
|
+
*/
|
|
3478
|
+
static isTrackArray(value: unknown): value is Track[];
|
|
3479
|
+
/**
|
|
3480
|
+
* Checks if the provided argument is a valid Track or Track array.
|
|
3481
|
+
* @param value The value to check.
|
|
3482
|
+
* @returns {boolean} Whether the provided argument is a valid Track or Track array.
|
|
3483
|
+
*/
|
|
3484
|
+
static validate(value: unknown): value is Track | Track[];
|
|
3470
3485
|
/**
|
|
3471
3486
|
* Builds a Track from the raw data from Lavalink and a optional requester.
|
|
3472
3487
|
* @param data The raw data from Lavalink to build the Track from.
|
|
3473
3488
|
* @param requester The user who requested the track, if any.
|
|
3489
|
+
* @param isAutoPlay Whether the track is autoplayed. Defaults to false.
|
|
3474
3490
|
* @returns The built Track.
|
|
3475
3491
|
*/
|
|
3476
|
-
static build<T = AnyUser>(data: TrackData, requester?: T): Track;
|
|
3492
|
+
static build<T = AnyUser>(data: TrackData, requester?: T, isAutoplay?: boolean): Track;
|
|
3477
3493
|
/**
|
|
3478
3494
|
* Validates a search result.
|
|
3479
3495
|
* @param result The search result to validate.
|
|
@@ -56,8 +56,7 @@ class JsonQueue {
|
|
|
56
56
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Added ${tracks.length} track(s) to queue`);
|
|
57
57
|
if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
|
|
58
58
|
if (!isArray) {
|
|
59
|
-
|
|
60
|
-
if (AutoplayUser && AutoplayUser.id === track.requester.id) {
|
|
59
|
+
if (track.isAutoplay) {
|
|
61
60
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
62
61
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
63
62
|
details: {
|
|
@@ -84,8 +84,7 @@ class MemoryQueue extends Array {
|
|
|
84
84
|
}
|
|
85
85
|
if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
|
|
86
86
|
if (!isArray) {
|
|
87
|
-
|
|
88
|
-
if (AutoplayUser && AutoplayUser.id === track.requester.id) {
|
|
87
|
+
if (track.isAutoplay) {
|
|
89
88
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
90
89
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
91
90
|
details: {
|
|
@@ -81,8 +81,7 @@ class RedisQueue {
|
|
|
81
81
|
// Autoplay logic
|
|
82
82
|
if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
|
|
83
83
|
if (!Array.isArray(track)) {
|
|
84
|
-
|
|
85
|
-
if (AutoplayUser && AutoplayUser.id === track.requester.id) {
|
|
84
|
+
if (track.isAutoplay) {
|
|
86
85
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
87
86
|
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
88
87
|
details: {
|
package/dist/structures/Enums.js
CHANGED
|
@@ -163,6 +163,8 @@ var TrackPartial;
|
|
|
163
163
|
TrackPartial["PluginInfo"] = "pluginInfo";
|
|
164
164
|
/** The custom data of the track */
|
|
165
165
|
TrackPartial["CustomData"] = "customData";
|
|
166
|
+
/** Whether the track got autoplayed */
|
|
167
|
+
TrackPartial["IsAutoPlay"] = "isAutoplay";
|
|
166
168
|
})(TrackPartial || (exports.TrackPartial = TrackPartial = {}));
|
|
167
169
|
/**
|
|
168
170
|
* Manager Event Types Enum
|
|
@@ -177,11 +177,15 @@ class Manager extends events_1.EventEmitter {
|
|
|
177
177
|
db: config.db ?? 0,
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
const results = await Promise.allSettled([...this.nodes.values()].map(async (node) => {
|
|
181
|
+
await node.connect();
|
|
182
|
+
return node;
|
|
183
|
+
}));
|
|
184
|
+
for (let i = 0; i < results.length; i++) {
|
|
185
|
+
const result = results[i];
|
|
186
|
+
const node = [...this.nodes.values()][i];
|
|
187
|
+
if (result.status === "rejected") {
|
|
188
|
+
const err = result.reason;
|
|
185
189
|
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
186
190
|
? err
|
|
187
191
|
: new MagmastreamError_1.MagmaStreamError({
|
|
@@ -277,9 +281,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
277
281
|
result.playlist.tracks = result.playlist.tracks.map(processTrack);
|
|
278
282
|
}
|
|
279
283
|
}
|
|
280
|
-
const summary = "tracks" in result
|
|
281
|
-
? result.tracks.map((t) => Object.fromEntries(Object.entries(t).filter(([key]) => key !== "requester")))
|
|
282
|
-
: [];
|
|
284
|
+
const summary = "tracks" in result ? result.tracks.map((t) => Object.fromEntries(Object.entries(t).filter(([key]) => key !== "requester"))) : [];
|
|
283
285
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Result search for ${_query.query}: ${Utils_1.JSONUtils.safe(summary, 2)}`);
|
|
284
286
|
return result;
|
|
285
287
|
}
|
|
@@ -468,7 +470,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
468
470
|
try {
|
|
469
471
|
const redisKey = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
470
472
|
? this.options.stateStorage.redisConfig.prefix
|
|
471
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:${guildId}`;
|
|
473
|
+
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:")}playerstore:${guildId}`;
|
|
472
474
|
await this.redis.set(redisKey, JSON.stringify(serializedPlayer));
|
|
473
475
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Player state saved to Redis: ${guildId}`);
|
|
474
476
|
}
|
|
@@ -547,6 +549,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
547
549
|
volume: lavaPlayer.volume || state.options.volume,
|
|
548
550
|
nodeIdentifier: nodeId,
|
|
549
551
|
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
552
|
+
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
550
553
|
};
|
|
551
554
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${Utils_1.JSONUtils.safe(state.options, 2)}`);
|
|
552
555
|
const player = this.create(playerOptions);
|
|
@@ -578,7 +581,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
578
581
|
if (lavaPlayer.track) {
|
|
579
582
|
await player.queue.clear();
|
|
580
583
|
if (currentTrack) {
|
|
581
|
-
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
|
|
584
|
+
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
582
585
|
}
|
|
583
586
|
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
584
587
|
if (remainingQueue.length > 0) {
|
|
@@ -721,7 +724,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
721
724
|
// Get all keys matching our pattern
|
|
722
725
|
const redisKeyPattern = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
723
726
|
? this.options.stateStorage.redisConfig.prefix
|
|
724
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:*`;
|
|
727
|
+
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:")}playerstore:*`;
|
|
725
728
|
const keys = await this.redis.keys(redisKeyPattern);
|
|
726
729
|
for (const key of keys) {
|
|
727
730
|
try {
|
|
@@ -746,6 +749,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
746
749
|
volume: lavaPlayer.volume || state.options.volume,
|
|
747
750
|
nodeIdentifier: nodeId,
|
|
748
751
|
applyVolumeAsFilter: state.options.applyVolumeAsFilter,
|
|
752
|
+
pauseOnDisconnect: state.options.pauseOnDisconnect,
|
|
749
753
|
};
|
|
750
754
|
this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
|
|
751
755
|
const player = this.create(playerOptions);
|
|
@@ -772,7 +776,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
772
776
|
if (lavaPlayer.track) {
|
|
773
777
|
await player.queue.clear();
|
|
774
778
|
if (currentTrack) {
|
|
775
|
-
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
|
|
779
|
+
await player.queue.add(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester, currentTrack.isAutoplay));
|
|
776
780
|
}
|
|
777
781
|
const remainingQueue = queueTracks.filter((t) => t.uri !== lavaPlayer.track.info.uri);
|
|
778
782
|
if (remainingQueue.length > 0) {
|
|
@@ -914,11 +918,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
914
918
|
* @returns {Node} The node to use.
|
|
915
919
|
*/
|
|
916
920
|
get useableNode() {
|
|
917
|
-
return this.options.enablePriorityMode
|
|
918
|
-
? this.priorityNode
|
|
919
|
-
: this.options.useNode === Enums_1.UseNodeOptions.LeastLoad
|
|
920
|
-
? this.leastLoadNode.first()
|
|
921
|
-
: this.leastPlayersNode.first();
|
|
921
|
+
return this.options.enablePriorityMode ? this.priorityNode : this.options.useNode === Enums_1.UseNodeOptions.LeastLoad ? this.leastLoadNode.first() : this.leastPlayersNode.first();
|
|
922
922
|
}
|
|
923
923
|
/**
|
|
924
924
|
* Handles the shutdown of the process by saving all active players' states and optionally cleaning up inactive players.
|
|
@@ -1105,6 +1105,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1105
1105
|
}
|
|
1106
1106
|
this.emit(Enums_1.ManagerEventTypes.PlayerDisconnect, player, player.voiceChannelId);
|
|
1107
1107
|
player.voiceChannelId = null;
|
|
1108
|
+
player.state = Enums_1.StateTypes.Disconnected;
|
|
1108
1109
|
player.voiceState = Object.assign({});
|
|
1109
1110
|
if (player.options.pauseOnDisconnect) {
|
|
1110
1111
|
await player.pause(true);
|
|
@@ -1154,7 +1155,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1154
1155
|
{
|
|
1155
1156
|
const prefix = this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
1156
1157
|
? this.options.stateStorage.redisConfig.prefix
|
|
1157
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:";
|
|
1158
|
+
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:");
|
|
1158
1159
|
const pattern = `${prefix}queue:*:current`;
|
|
1159
1160
|
try {
|
|
1160
1161
|
const stream = this.redis.scanStream({
|
|
@@ -1339,7 +1340,7 @@ class Manager extends events_1.EventEmitter {
|
|
|
1339
1340
|
case Enums_1.StateStorageType.Redis: {
|
|
1340
1341
|
const prefix = this.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
1341
1342
|
? this.options.stateStorage.redisConfig.prefix
|
|
1342
|
-
: this.options.stateStorage.redisConfig.prefix ?? "magmastream:";
|
|
1343
|
+
: (this.options.stateStorage.redisConfig.prefix ?? "magmastream:");
|
|
1343
1344
|
const patterns = [`${prefix}playerstore:*`, `${prefix}queue:*`];
|
|
1344
1345
|
try {
|
|
1345
1346
|
for (const pattern of patterns) {
|
package/dist/structures/Node.js
CHANGED
|
@@ -108,7 +108,7 @@ class Node {
|
|
|
108
108
|
case Enums_1.StateStorageType.Redis:
|
|
109
109
|
this.redisPrefix = this.manager.options.stateStorage.redisConfig.prefix?.endsWith(":")
|
|
110
110
|
? this.manager.options.stateStorage.redisConfig.prefix
|
|
111
|
-
: this.manager.options.stateStorage.redisConfig.prefix ?? "magmastream:";
|
|
111
|
+
: (this.manager.options.stateStorage.redisConfig.prefix ?? "magmastream:");
|
|
112
112
|
break;
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -632,14 +632,13 @@ class Node {
|
|
|
632
632
|
player.playing = true;
|
|
633
633
|
player.paused = false;
|
|
634
634
|
this.manager.emit(Enums_1.ManagerEventTypes.TrackStart, player, track, payload);
|
|
635
|
-
|
|
636
|
-
if (AutoplayUser && AutoplayUser.id === track.requester.id) {
|
|
635
|
+
if (track.isAutoplay) {
|
|
637
636
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, player, {
|
|
638
637
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
639
638
|
details: {
|
|
640
639
|
type: "track",
|
|
641
640
|
action: "autoPlay",
|
|
642
|
-
track
|
|
641
|
+
track,
|
|
643
642
|
},
|
|
644
643
|
});
|
|
645
644
|
return;
|
|
@@ -649,7 +648,7 @@ class Node {
|
|
|
649
648
|
details: {
|
|
650
649
|
type: "track",
|
|
651
650
|
action: "start",
|
|
652
|
-
track
|
|
651
|
+
track,
|
|
653
652
|
},
|
|
654
653
|
});
|
|
655
654
|
}
|
|
@@ -222,7 +222,9 @@ class Player {
|
|
|
222
222
|
// Clone the current player state for comparison.
|
|
223
223
|
const oldPlayer = this ? { ...this } : null;
|
|
224
224
|
// Pause the player.
|
|
225
|
-
|
|
225
|
+
if (this.options.pauseOnDisconnect) {
|
|
226
|
+
await this.pause(true);
|
|
227
|
+
}
|
|
226
228
|
// Send the voice state update to the gateway.
|
|
227
229
|
this.manager.sendPacket({
|
|
228
230
|
op: 4,
|
|
@@ -258,6 +260,9 @@ class Player {
|
|
|
258
260
|
*/
|
|
259
261
|
async destroy(disconnect = true) {
|
|
260
262
|
this.state = Enums_1.StateTypes.Destroying;
|
|
263
|
+
await this.queue.clear();
|
|
264
|
+
await this.queue.clearPrevious();
|
|
265
|
+
await this.queue.setCurrent(null);
|
|
261
266
|
if (disconnect) {
|
|
262
267
|
await this.disconnect().catch((err) => {
|
|
263
268
|
console.warn(`[Player#destroy] Failed to disconnect player ${this.guildId}:`, err);
|
|
@@ -266,9 +271,6 @@ class Player {
|
|
|
266
271
|
await this.node.rest.destroyPlayer(this.guildId).catch((err) => {
|
|
267
272
|
console.warn(`[Player#destroy] REST failed to destroy player ${this.guildId}:`, err);
|
|
268
273
|
});
|
|
269
|
-
await this.queue.clear();
|
|
270
|
-
await this.queue.clearPrevious();
|
|
271
|
-
await this.queue.setCurrent(null);
|
|
272
274
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerDestroy, this);
|
|
273
275
|
const deleted = this.manager.players.delete(this.guildId);
|
|
274
276
|
if (this.manager.options.stateStorage.deleteInactivePlayers)
|
|
@@ -804,11 +806,8 @@ class Player {
|
|
|
804
806
|
if (currentPlayingTrack) {
|
|
805
807
|
await this.queue.add(currentPlayingTrack, 0);
|
|
806
808
|
}
|
|
807
|
-
await this.play(lastTrack);
|
|
808
|
-
}
|
|
809
|
-
else {
|
|
810
|
-
await this.play(lastTrack);
|
|
811
809
|
}
|
|
810
|
+
await this.play(lastTrack);
|
|
812
811
|
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this, {
|
|
813
812
|
changeType: Enums_1.PlayerStateEventTypes.TrackChange,
|
|
814
813
|
details: {
|
|
@@ -1003,6 +1002,7 @@ class Player {
|
|
|
1003
1002
|
nowPlayingMessage: this.nowPlayingMessage,
|
|
1004
1003
|
isAutoplay: this.isAutoplay,
|
|
1005
1004
|
applyVolumeAsFilter: this.options.applyVolumeAsFilter,
|
|
1005
|
+
pauseOnDisconnect: this.options.pauseOnDisconnect,
|
|
1006
1006
|
};
|
|
1007
1007
|
// If force is true, destroy the existing player for the new guild
|
|
1008
1008
|
if (force && newPlayer) {
|
|
@@ -1013,6 +1013,7 @@ class Player {
|
|
|
1013
1013
|
newOptions.selfMute = newOptions.selfMute ?? oldPlayerProperties.selfMute;
|
|
1014
1014
|
newOptions.volume = newOptions.volume ?? oldPlayerProperties.volume;
|
|
1015
1015
|
newOptions.applyVolumeAsFilter = newOptions.applyVolumeAsFilter ?? oldPlayerProperties.applyVolumeAsFilter;
|
|
1016
|
+
newOptions.pauseOnDisconnect = newOptions.pauseOnDisconnect ?? oldPlayerProperties.pauseOnDisconnect;
|
|
1016
1017
|
// Deep clone the current player
|
|
1017
1018
|
const clonedPlayer = this.manager.create(newOptions);
|
|
1018
1019
|
// Connect the cloned player to the new voice channel
|
package/dist/structures/Rest.js
CHANGED
|
@@ -104,35 +104,55 @@ class Rest {
|
|
|
104
104
|
},
|
|
105
105
|
data: body,
|
|
106
106
|
timeout: this.node.options.apiRequestTimeoutMs,
|
|
107
|
+
validateStatus: () => true,
|
|
107
108
|
};
|
|
109
|
+
let response;
|
|
108
110
|
try {
|
|
109
|
-
|
|
110
|
-
return response.data;
|
|
111
|
+
response = await (0, axios_1.default)(config);
|
|
111
112
|
}
|
|
112
113
|
catch (err) {
|
|
113
|
-
const
|
|
114
|
-
if (!error.response) {
|
|
115
|
-
throw new MagmastreamError_1.MagmaStreamError({
|
|
116
|
-
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
117
|
-
message: `No response from node ${this.node.options.identifier}: ${error.message}`,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
const data = error.response.data;
|
|
121
|
-
if (data?.message === "Guild not found") {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
if (error.response.status === 401) {
|
|
125
|
-
throw new MagmastreamError_1.MagmaStreamError({
|
|
126
|
-
code: Enums_1.MagmaStreamErrorCode.REST_UNAUTHORIZED,
|
|
127
|
-
message: `Unauthorized access to node ${this.node.options.identifier}`,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
const dataMessage = typeof data === "string" ? data : data?.message ? data.message : Utils_1.JSONUtils.safe(data, 2);
|
|
114
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
131
115
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
132
116
|
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
133
|
-
message: `
|
|
117
|
+
message: `No response from node ${this.node.options.identifier}: ${message}`,
|
|
134
118
|
});
|
|
135
119
|
}
|
|
120
|
+
const { status, data } = response;
|
|
121
|
+
if (status >= 200 && status < 300) {
|
|
122
|
+
return data;
|
|
123
|
+
}
|
|
124
|
+
// Lavalink sometimes returns "Guild not found" for inactive players
|
|
125
|
+
if (status === 404 && data?.message === "Guild not found") {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
if (status === 401) {
|
|
129
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
130
|
+
code: Enums_1.MagmaStreamErrorCode.REST_UNAUTHORIZED,
|
|
131
|
+
message: `Unauthorized access to node ${this.node.options.identifier}`,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (status >= 400 && status < 500) {
|
|
135
|
+
const message = typeof data === "string"
|
|
136
|
+
? data
|
|
137
|
+
: typeof data === "object" && data !== null && "message" in data && typeof data.message === "string"
|
|
138
|
+
? data.message
|
|
139
|
+
: "Unknown client error";
|
|
140
|
+
return {
|
|
141
|
+
status,
|
|
142
|
+
error: true,
|
|
143
|
+
message,
|
|
144
|
+
data,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const safeMessage = typeof data === "string"
|
|
148
|
+
? data
|
|
149
|
+
: typeof data === "object" && data !== null && "message" in data && typeof data.message === "string"
|
|
150
|
+
? data.message
|
|
151
|
+
: Utils_1.JSONUtils.safe(data, 2);
|
|
152
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
153
|
+
code: Enums_1.MagmaStreamErrorCode.REST_REQUEST_FAILED,
|
|
154
|
+
message: `Request to node ${this.node.options.identifier} failed (${status}): ${safeMessage}`,
|
|
155
|
+
});
|
|
136
156
|
}
|
|
137
157
|
/**
|
|
138
158
|
* Sends a GET request to the specified endpoint and returns the response data.
|
package/dist/structures/Utils.js
CHANGED
|
@@ -9,9 +9,11 @@ const Enums_1 = require("./Enums");
|
|
|
9
9
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
10
10
|
const safe_stable_stringify_1 = tslib_1.__importDefault(require("safe-stable-stringify"));
|
|
11
11
|
const MagmastreamError_1 = require("./MagmastreamError");
|
|
12
|
+
const lodash_1 = require("lodash");
|
|
12
13
|
// import playwright from "playwright";
|
|
13
14
|
/** @hidden */
|
|
14
15
|
const SIZES = ["0", "1", "2", "3", "default", "mqdefault", "hqdefault", "maxresdefault"];
|
|
16
|
+
const REQUIRED_TRACK_KEYS = ["track", "title", "uri"];
|
|
15
17
|
class TrackUtils {
|
|
16
18
|
static trackPartial = null;
|
|
17
19
|
static manager;
|
|
@@ -39,19 +41,12 @@ class TrackUtils {
|
|
|
39
41
|
const defaultProperties = [
|
|
40
42
|
Enums_1.TrackPartial.Track,
|
|
41
43
|
Enums_1.TrackPartial.Title,
|
|
42
|
-
Enums_1.TrackPartial.Identifier,
|
|
43
44
|
Enums_1.TrackPartial.Author,
|
|
44
45
|
Enums_1.TrackPartial.Duration,
|
|
45
|
-
Enums_1.TrackPartial.Isrc,
|
|
46
|
-
Enums_1.TrackPartial.IsSeekable,
|
|
47
|
-
Enums_1.TrackPartial.IsStream,
|
|
48
46
|
Enums_1.TrackPartial.Uri,
|
|
49
|
-
Enums_1.TrackPartial.ArtworkUrl,
|
|
50
47
|
Enums_1.TrackPartial.SourceName,
|
|
51
|
-
Enums_1.TrackPartial.
|
|
48
|
+
Enums_1.TrackPartial.ArtworkUrl,
|
|
52
49
|
Enums_1.TrackPartial.Requester,
|
|
53
|
-
Enums_1.TrackPartial.PluginInfo,
|
|
54
|
-
Enums_1.TrackPartial.CustomData,
|
|
55
50
|
];
|
|
56
51
|
/** The array of property names that will be removed from the Track class */
|
|
57
52
|
this.trackPartial = Array.from(new Set([...defaultProperties, ...partial]));
|
|
@@ -61,33 +56,39 @@ class TrackUtils {
|
|
|
61
56
|
}
|
|
62
57
|
/**
|
|
63
58
|
* Checks if the provided argument is a valid Track.
|
|
64
|
-
*
|
|
65
|
-
* @param trackOrTracks The Track or array of Tracks to check.
|
|
59
|
+
* @param value The value to check.
|
|
66
60
|
* @returns {boolean} Whether the provided argument is a valid Track.
|
|
67
61
|
*/
|
|
68
|
-
static
|
|
69
|
-
if (typeof
|
|
62
|
+
static isTrack(track) {
|
|
63
|
+
if (typeof track !== "object" || track === null)
|
|
70
64
|
return false;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
const t = track;
|
|
66
|
+
return REQUIRED_TRACK_KEYS.every((key) => typeof t[key] === "string");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Checks if the provided argument is a valid Track array.
|
|
70
|
+
* @param value The value to check.
|
|
71
|
+
* @returns {boolean} Whether the provided argument is a valid Track array.
|
|
72
|
+
*/
|
|
73
|
+
static isTrackArray(value) {
|
|
74
|
+
return Array.isArray(value) && value.every(this.isTrack);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Checks if the provided argument is a valid Track or Track array.
|
|
78
|
+
* @param value The value to check.
|
|
79
|
+
* @returns {boolean} Whether the provided argument is a valid Track or Track array.
|
|
80
|
+
*/
|
|
81
|
+
static validate(value) {
|
|
82
|
+
return this.isTrack(value) || this.isTrackArray(value);
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
85
|
* Builds a Track from the raw data from Lavalink and a optional requester.
|
|
86
86
|
* @param data The raw data from Lavalink to build the Track from.
|
|
87
87
|
* @param requester The user who requested the track, if any.
|
|
88
|
+
* @param isAutoPlay Whether the track is autoplayed. Defaults to false.
|
|
88
89
|
* @returns The built Track.
|
|
89
90
|
*/
|
|
90
|
-
static build(data, requester) {
|
|
91
|
+
static build(data, requester, isAutoplay = false) {
|
|
91
92
|
if (typeof data === "undefined") {
|
|
92
93
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
93
94
|
code: Enums_1.MagmaStreamErrorCode.UTILS_TRACK_BUILD_FAILED,
|
|
@@ -138,6 +139,7 @@ class TrackUtils {
|
|
|
138
139
|
requester: requester,
|
|
139
140
|
pluginInfo: data.pluginInfo,
|
|
140
141
|
customData: {},
|
|
142
|
+
isAutoplay: isAutoplay,
|
|
141
143
|
};
|
|
142
144
|
track.displayThumbnail = track.displayThumbnail.bind(track);
|
|
143
145
|
if (this.trackPartial) {
|
|
@@ -542,8 +544,7 @@ class AutoPlayUtils {
|
|
|
542
544
|
return typeof data === "object" && data !== null && "encoded" in data && "info" in data;
|
|
543
545
|
}
|
|
544
546
|
static isTrackDataArray(data) {
|
|
545
|
-
return (Array.isArray(data) &&
|
|
546
|
-
data.every((track) => typeof track === "object" && track !== null && "encoded" in track && "info" in track && typeof track.encoded === "string"));
|
|
547
|
+
return (Array.isArray(data) && data.every((track) => typeof track === "object" && track !== null && "encoded" in track && "info" in track && typeof track.encoded === "string"));
|
|
547
548
|
}
|
|
548
549
|
static buildTracksFromResponse(recommendedResult, requester) {
|
|
549
550
|
if (!recommendedResult)
|
|
@@ -560,7 +561,7 @@ class AutoPlayUtils {
|
|
|
560
561
|
context: { recommendedResult },
|
|
561
562
|
});
|
|
562
563
|
}
|
|
563
|
-
return [TrackUtils.build(data, requester)];
|
|
564
|
+
return [TrackUtils.build(data, requester, true)];
|
|
564
565
|
}
|
|
565
566
|
case Enums_1.LoadTypes.Short:
|
|
566
567
|
case Enums_1.LoadTypes.Search: {
|
|
@@ -572,7 +573,7 @@ class AutoPlayUtils {
|
|
|
572
573
|
context: { recommendedResult },
|
|
573
574
|
});
|
|
574
575
|
}
|
|
575
|
-
return data.map((d) => TrackUtils.build(d, requester));
|
|
576
|
+
return data.map((d) => TrackUtils.build(d, requester, true));
|
|
576
577
|
}
|
|
577
578
|
case Enums_1.LoadTypes.Album:
|
|
578
579
|
case Enums_1.LoadTypes.Artist:
|
|
@@ -582,7 +583,7 @@ class AutoPlayUtils {
|
|
|
582
583
|
case Enums_1.LoadTypes.Playlist: {
|
|
583
584
|
const data = recommendedResult.data;
|
|
584
585
|
if (this.isPlaylistRawData(data)) {
|
|
585
|
-
return data.tracks.map((d) => TrackUtils.build(d, requester));
|
|
586
|
+
return data.tracks.map((d) => TrackUtils.build(d, requester, true));
|
|
586
587
|
}
|
|
587
588
|
throw new MagmastreamError_1.MagmaStreamError({
|
|
588
589
|
code: Enums_1.MagmaStreamErrorCode.UTILS_AUTOPLAY_BUILD_FAILED,
|
|
@@ -622,65 +623,89 @@ class PlayerUtils {
|
|
|
622
623
|
* @returns The serialized Player instance
|
|
623
624
|
*/
|
|
624
625
|
static async serializePlayer(player) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
return undefined;
|
|
637
|
-
return value;
|
|
638
|
-
}))
|
|
639
|
-
: null;
|
|
640
|
-
return JSON.parse(JSON.stringify(player, (key, value) => {
|
|
641
|
-
if (key === "manager")
|
|
642
|
-
return null;
|
|
643
|
-
if (key === "node")
|
|
644
|
-
return safeNode;
|
|
645
|
-
if (key === "filters") {
|
|
646
|
-
return {
|
|
647
|
-
distortion: value?.distortion ?? null,
|
|
648
|
-
equalizer: value?.equalizer ?? [],
|
|
649
|
-
karaoke: value?.karaoke ?? null,
|
|
650
|
-
rotation: value?.rotation ?? null,
|
|
651
|
-
timescale: value?.timescale ?? null,
|
|
652
|
-
vibrato: value?.vibrato ?? null,
|
|
653
|
-
reverb: value?.reverb ?? null,
|
|
654
|
-
volume: value?.volume ?? 1.0,
|
|
655
|
-
bassBoostlevel: value?.bassBoostlevel ?? null,
|
|
656
|
-
filterStatus: value?.filtersStatus ? { ...value.filtersStatus } : {},
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
if (key === "queue") {
|
|
660
|
-
return {
|
|
661
|
-
current: current ? serializeTrack(current) : null,
|
|
662
|
-
tracks: tracks.map(serializeTrack),
|
|
663
|
-
previous: previous.map(serializeTrack),
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
if (key === "data") {
|
|
667
|
-
return {
|
|
668
|
-
clientUser: value?.Internal_AutoplayUser ?? null,
|
|
669
|
-
nowPlayingMessage: value?.nowPlayingMessage ?? null,
|
|
670
|
-
};
|
|
671
|
-
}
|
|
626
|
+
const current = await player.queue.getCurrent();
|
|
627
|
+
const tracks = await player.queue.getTracks();
|
|
628
|
+
const previous = await player.queue.getPrevious();
|
|
629
|
+
const serializeTrack = (track) => ({
|
|
630
|
+
...track,
|
|
631
|
+
requester: track.requester ? { id: track.requester.id, username: track.requester.username } : null,
|
|
632
|
+
});
|
|
633
|
+
const safeNode = player.node
|
|
634
|
+
? JSON.parse(JSON.stringify(player.node, (key, value) => {
|
|
635
|
+
if (key === "rest" || key === "players" || key === "shards" || key === "manager")
|
|
636
|
+
return undefined;
|
|
672
637
|
return value;
|
|
673
|
-
}))
|
|
638
|
+
}))
|
|
639
|
+
: null;
|
|
640
|
+
const isNonSerializable = (value) => {
|
|
641
|
+
if (typeof value === "function" || typeof value === "symbol")
|
|
642
|
+
return true;
|
|
643
|
+
if (typeof value === "object" && value !== null) {
|
|
644
|
+
const ctorName = value.constructor?.name ?? "";
|
|
645
|
+
return (value instanceof Map ||
|
|
646
|
+
value instanceof Set ||
|
|
647
|
+
value instanceof WeakMap ||
|
|
648
|
+
value instanceof WeakSet ||
|
|
649
|
+
ctorName === "Timeout" ||
|
|
650
|
+
ctorName === "Socket" ||
|
|
651
|
+
ctorName === "TLSSocket" ||
|
|
652
|
+
ctorName === "EventEmitter");
|
|
653
|
+
}
|
|
654
|
+
return false;
|
|
655
|
+
};
|
|
656
|
+
const safeReplacer = (key, value) => {
|
|
657
|
+
if (isNonSerializable(value))
|
|
658
|
+
return undefined;
|
|
659
|
+
if (key === "manager")
|
|
660
|
+
return null;
|
|
661
|
+
if (key === "node")
|
|
662
|
+
return safeNode;
|
|
663
|
+
if (key === "filters") {
|
|
664
|
+
const filters = { ...value };
|
|
665
|
+
delete filters.player;
|
|
666
|
+
return {
|
|
667
|
+
distortion: filters["distortion"] ?? null,
|
|
668
|
+
equalizer: filters["equalizer"] ?? [],
|
|
669
|
+
karaoke: filters["karaoke"] ?? null,
|
|
670
|
+
rotation: filters["rotation"] ?? null,
|
|
671
|
+
timescale: value["timescale"] ?? null,
|
|
672
|
+
vibrato: value["vibrato"] ?? null,
|
|
673
|
+
reverb: value["reverb"] ?? null,
|
|
674
|
+
volume: value["volume"] ?? 1.0,
|
|
675
|
+
bassBoostlevel: value["bassBoostlevel"] ?? null,
|
|
676
|
+
filterStatus: (0, lodash_1.isPlainObject)(value["filtersStatus"]) ? { ...value["filtersStatus"] } : {},
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (key === "queue") {
|
|
680
|
+
return {
|
|
681
|
+
current: current ? serializeTrack(current) : null,
|
|
682
|
+
tracks: tracks.map(serializeTrack),
|
|
683
|
+
previous: previous.map(serializeTrack),
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
if (key === "data" && (0, lodash_1.isPlainObject)(value)) {
|
|
687
|
+
return {
|
|
688
|
+
clientUser: value["Internal_AutoplayUser"] ?? null,
|
|
689
|
+
nowPlayingMessage: value["nowPlayingMessage"] ?? null,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
return value;
|
|
693
|
+
};
|
|
694
|
+
let serialized;
|
|
695
|
+
try {
|
|
696
|
+
serialized = JSON.stringify(player, safeReplacer);
|
|
674
697
|
}
|
|
675
698
|
catch (err) {
|
|
676
|
-
|
|
699
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
677
700
|
? err
|
|
678
701
|
: new MagmastreamError_1.MagmaStreamError({
|
|
679
702
|
code: Enums_1.MagmaStreamErrorCode.MANAGER_SEARCH_FAILED,
|
|
680
703
|
message: `An error occurred while searching: ${err instanceof Error ? err.message : String(err)}`,
|
|
681
704
|
cause: err instanceof Error ? err : undefined,
|
|
682
705
|
});
|
|
706
|
+
console.error(error);
|
|
683
707
|
}
|
|
708
|
+
return JSON.parse(serialized);
|
|
684
709
|
}
|
|
685
710
|
/**
|
|
686
711
|
* Gets the base directory for player data.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magmastream",
|
|
3
|
-
"version": "2.9.3-dev.
|
|
3
|
+
"version": "2.9.3-dev.10",
|
|
4
4
|
"description": "A user-friendly Lavalink client designed for NodeJS.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,45 +10,48 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
12
|
"types": "rtb --dist dist",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
13
|
+
"format": "prettier --write .",
|
|
14
|
+
"format:check": "prettier --check .",
|
|
15
|
+
"lint": "eslint \"src/**/*.{ts,js}\"",
|
|
16
|
+
"lint:fix": "eslint --fix \"src/**/*.{ts,js}\"",
|
|
17
|
+
"ci": "run-s format:check lint build types",
|
|
18
|
+
"release:dev": "npm run format && npm run lint:fix && npm run ci && npm version prerelease --preid=dev && npm publish --tag dev"
|
|
17
19
|
},
|
|
18
20
|
"devDependencies": {
|
|
19
21
|
"@favware/rollup-type-bundler": "^4.0.0",
|
|
20
|
-
"@types/jsdom": "^
|
|
21
|
-
"@types/lodash": "^4.17.
|
|
22
|
-
"@types/node": "^
|
|
22
|
+
"@types/jsdom": "^27.0.0",
|
|
23
|
+
"@types/lodash": "^4.17.23",
|
|
24
|
+
"@types/node": "^25.0.9",
|
|
23
25
|
"@types/ws": "^8.18.1",
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
25
|
-
"@typescript-eslint/parser": "^8.
|
|
26
|
-
"eslint": "^9.
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
27
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
28
|
+
"eslint": "^9.39.2",
|
|
27
29
|
"npm-run-all": "^4.1.5",
|
|
28
|
-
"
|
|
30
|
+
"prettier": "^3.8.0",
|
|
31
|
+
"typedoc": "^0.28.16",
|
|
29
32
|
"typedoc-plugin-no-inherit": "^1.6.1",
|
|
30
|
-
"typescript": "^5.
|
|
33
|
+
"typescript": "^5.9.3"
|
|
31
34
|
},
|
|
32
35
|
"dependencies": {
|
|
33
36
|
"@discordjs/collection": "^2.1.1",
|
|
34
|
-
"axios": "^1.
|
|
37
|
+
"axios": "^1.13.2",
|
|
35
38
|
"events": "^3.3.0",
|
|
36
|
-
"ioredis": "^5.
|
|
37
|
-
"jsdom": "^
|
|
39
|
+
"ioredis": "^5.9.2",
|
|
40
|
+
"jsdom": "^27.4.0",
|
|
38
41
|
"lodash": "^4.17.21",
|
|
39
42
|
"safe-stable-stringify": "^2.5.0",
|
|
40
43
|
"tslib": "^2.8.1",
|
|
41
|
-
"ws": "^8.
|
|
44
|
+
"ws": "^8.19.0"
|
|
42
45
|
},
|
|
43
46
|
"optionalDependencies": {
|
|
44
47
|
"detritus-client": "0.16.x",
|
|
45
48
|
"discord.js": "14.x",
|
|
46
49
|
"eris": "0.18.x",
|
|
47
|
-
"oceanic.js": "1.
|
|
48
|
-
"seyfert": "
|
|
50
|
+
"oceanic.js": "^1.13.0",
|
|
51
|
+
"seyfert": "4.0.x"
|
|
49
52
|
},
|
|
50
53
|
"engines": {
|
|
51
|
-
"node": ">=
|
|
54
|
+
"node": ">=20.19.0"
|
|
52
55
|
},
|
|
53
56
|
"eslintConfig": {
|
|
54
57
|
"root": true,
|
|
@@ -92,7 +95,8 @@
|
|
|
92
95
|
"magmastream"
|
|
93
96
|
],
|
|
94
97
|
"repository": {
|
|
95
|
-
"
|
|
98
|
+
"type": "git",
|
|
99
|
+
"url": "git+https://gitryx.com/MagmaStream/magmastream.git#main"
|
|
96
100
|
},
|
|
97
101
|
"homepage": "https://docs.magmastream.com",
|
|
98
102
|
"author": "Abel Purnwasy",
|