magmastream 2.9.2-dev.1 → 2.9.2-dev.3
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
package/dist/index.d.ts
CHANGED
|
@@ -275,6 +275,53 @@ declare enum AvailableFilters {
|
|
|
275
275
|
Vaporwave = "vaporwave",
|
|
276
276
|
Vibrato = "vibrato"
|
|
277
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* MagmaStream Error Codes Enum
|
|
280
|
+
*/
|
|
281
|
+
declare enum MagmaStreamErrorCode {
|
|
282
|
+
GENERAL_UNKNOWN = "MS_GENERAL_UNKNOWN",
|
|
283
|
+
GENERAL_TIMEOUT = "MS_GENERAL_TIMEOUT",
|
|
284
|
+
GENERAL_INVALID_MANAGER = "MS_GENERAL_INVALID_MANAGER",
|
|
285
|
+
MANAGER_INIT_FAILED = "MS_MANAGER_INIT_FAILED",
|
|
286
|
+
MANAGER_INVALID_CONFIG = "MS_MANAGER_INVALID_CONFIG",
|
|
287
|
+
MANAGER_SHUTDOWN_FAILED = "MS_MANAGER_SHUTDOWN_FAILED",
|
|
288
|
+
MANAGER_NO_NODES = "MS_MANAGER_NO_NODES",
|
|
289
|
+
MANAGER_NODE_NOT_FOUND = "MS_MANAGER_NODE_NOT_FOUND",
|
|
290
|
+
MANAGER_SEARCH_FAILED = "MS_MANAGER_SEARCH_FAILED",
|
|
291
|
+
MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED = "MS_MANAGER_CLEANUP_INACTIVE_PLAYERS_FAILED",
|
|
292
|
+
NODE_INVALID_CONFIG = "MS_NODE_INVALID_CONFIG",
|
|
293
|
+
NODE_CONNECT_FAILED = "MS_NODE_CONNECT_FAILED",
|
|
294
|
+
NODE_RECONNECT_FAILED = "MS_NODE_RECONNECT_FAILED",
|
|
295
|
+
NODE_DISCONNECTED = "MS_NODE_DISCONNECTED",
|
|
296
|
+
NODE_PROTOCOL_ERROR = "MS_NODE_PROTOCOL_ERROR",
|
|
297
|
+
NODE_SESSION_IDS_LOAD_FAILED = "MS_NODE_SESSION_IDS_LOAD_FAILED",
|
|
298
|
+
NODE_SESSION_IDS_UPDATE_FAILED = "MS_NODE_SESSION_IDS_UPDATE_FAILED",
|
|
299
|
+
NODE_PLUGIN_ERROR = "MS_NODE_PLUGIN_ERROR",
|
|
300
|
+
PLAYER_INVALID_CONFIG = "MS_PLAYER_INVALID_CONFIG",
|
|
301
|
+
PLAYER_STATE_INVALID = "MS_PLAYER_STATE_INVALID",
|
|
302
|
+
PLAYER_QUEUE_EMPTY = "MS_PLAYER_QUEUE_EMPTY",
|
|
303
|
+
PLAYER_PREVIOUS_EMPTY = "MS_PLAYER_PREVIOUS_EMPTY",
|
|
304
|
+
PLAYER_INVALID_NOW_PLAYING_MESSAGE = "MS_PLAYER_INVALID_NOW_PLAYING_MESSAGE",
|
|
305
|
+
PLAYER_INVALID_AUTOPLAY = "MS_PLAYER_INVALID_AUTOPLAY",
|
|
306
|
+
PLAYER_INVALID_VOLUME = "MS_PLAYER_INVALID_VOLUME",
|
|
307
|
+
PLAYER_INVALID_REPEAT = "MS_PLAYER_INVALID_REPEAT",
|
|
308
|
+
PLAYER_INVALID_PAUSE = "MS_PLAYER_INVALID_PAUSE",
|
|
309
|
+
PLAYER_INVALID_SEEK = "MS_PLAYER_INVALID_SEEK",
|
|
310
|
+
PLAYER_MOVE_FAILED = "MS_PLAYER_MOVE_FAILED",
|
|
311
|
+
PLAYER_VOICE_RECEIVER_ERROR = "MS_PLAYER_VOICE_RECEIVER_ERROR",
|
|
312
|
+
QUEUE_REDIS_ERROR = "MS_QUEUE_REDIS_ERROR",
|
|
313
|
+
QUEUE_JSON_ERROR = "MS_QUEUE_JSON_ERROR",
|
|
314
|
+
QUEUE_MEMORY_ERROR = "MS_QUEUE_MEMORY_ERROR",
|
|
315
|
+
FILTER_APPLY_FAILED = "MS_FILTER_APPLY_FAILED",
|
|
316
|
+
REST_REQUEST_FAILED = "MS_REST_REQUEST_FAILED",
|
|
317
|
+
REST_UNAUTHORIZED = "MS_REST_UNAUTHORIZED",
|
|
318
|
+
UTILS_TRACK_PARTIAL_INVALID = "MS_UTILS_TRACK_PARTIAL_INVALID",
|
|
319
|
+
UTILS_TRACK_BUILD_FAILED = "MS_UTILS_TRACK_BUILD_FAILED",
|
|
320
|
+
UTILS_AUTOPLAY_BUILD_FAILED = "MS_UTILS_AUTOPLAY_BUILD_FAILED",
|
|
321
|
+
PLUGIN_LOAD_FAILED = "MS_PLUGIN_LOAD_FAILED",
|
|
322
|
+
PLUGIN_RUNTIME_ERROR = "MS_PLUGIN_RUNTIME_ERROR"
|
|
323
|
+
}
|
|
324
|
+
declare const MagmaStreamErrorNumbers: Record<MagmaStreamErrorCode, number>;
|
|
278
325
|
|
|
279
326
|
/**
|
|
280
327
|
* The player's queue, the `current` property is the currently playing track, think of the rest as the up-coming tracks.
|
|
@@ -2262,7 +2309,6 @@ declare class Player {
|
|
|
2262
2309
|
* Retrieves the current lyrics for the playing track.
|
|
2263
2310
|
* @param skipTrackSource - Indicates whether to skip the track source when fetching lyrics.
|
|
2264
2311
|
* @returns {Promise<Lyrics>} - The lyrics of the current track.
|
|
2265
|
-
* @throws {RangeError} - If the 'lavalyrics-plugin' is not available on the Lavalink node.
|
|
2266
2312
|
*/
|
|
2267
2313
|
getCurrentLyrics(skipTrackSource?: boolean): Promise<Lyrics>;
|
|
2268
2314
|
/**
|
|
@@ -2336,10 +2382,10 @@ declare class Rest {
|
|
|
2336
2382
|
*/
|
|
2337
2383
|
setSessionId(sessionId: string): string;
|
|
2338
2384
|
/**
|
|
2339
|
-
* Retrieves
|
|
2385
|
+
* Retrieves one the player that is currently running on the node.
|
|
2340
2386
|
* @returns {Promise<unknown>} Returns the result of the GET request.
|
|
2341
2387
|
*/
|
|
2342
|
-
|
|
2388
|
+
getPlayer(guildId: string): Promise<LavaPlayer>;
|
|
2343
2389
|
/**
|
|
2344
2390
|
* Sends a PATCH request to update player related data.
|
|
2345
2391
|
* @param {RestPlayOptions} options The options to update the player with.
|
|
@@ -3525,6 +3571,19 @@ declare abstract class JSONUtils {
|
|
|
3525
3571
|
static serializeTrack(track: Track): string;
|
|
3526
3572
|
}
|
|
3527
3573
|
|
|
3574
|
+
interface MagmaStreamErrorOptions<T = unknown> {
|
|
3575
|
+
code: MagmaStreamErrorCode;
|
|
3576
|
+
message?: string;
|
|
3577
|
+
cause?: Error;
|
|
3578
|
+
context?: T;
|
|
3579
|
+
}
|
|
3580
|
+
declare class MagmaStreamError<T = unknown> extends Error {
|
|
3581
|
+
readonly code: MagmaStreamErrorCode;
|
|
3582
|
+
readonly number: number;
|
|
3583
|
+
readonly context?: T;
|
|
3584
|
+
constructor({ code, message, cause, context }: MagmaStreamErrorOptions<T>);
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3528
3587
|
/**
|
|
3529
3588
|
* Discord.js wrapper for Magmastream.
|
|
3530
3589
|
*/
|
|
@@ -3597,5 +3656,5 @@ declare class SeyfertManager extends Manager {
|
|
|
3597
3656
|
resolveUser(user: PortableUser | string): Promise<User$3 | PortableUser>;
|
|
3598
3657
|
}
|
|
3599
3658
|
|
|
3600
|
-
export { AutoPlayPlatform, AutoPlayUtils, AvailableFilters, DetritusManager, DiscordJSManager, ErisManager, Filters, JSONUtils, JsonQueue, LoadTypes, Manager, ManagerEventTypes, MemoryQueue, Node, OceanicManager, Player, PlayerStateEventTypes, PlayerUtils, Plugin, RedisQueue, Rest, SearchPlatform, SeverityTypes, SeyfertManager, SponsorBlockSegment, StateStorageType, StateTypes, Structure, TrackEndReasonTypes, TrackPartial, TrackSourceTypes, TrackUtils, UseNodeOptions };
|
|
3659
|
+
export { AutoPlayPlatform, AutoPlayUtils, AvailableFilters, DetritusManager, DiscordJSManager, ErisManager, Filters, JSONUtils, JsonQueue, LoadTypes, MagmaStreamError, MagmaStreamErrorCode, MagmaStreamErrorNumbers, Manager, ManagerEventTypes, MemoryQueue, Node, OceanicManager, Player, PlayerStateEventTypes, PlayerUtils, Plugin, RedisQueue, Rest, SearchPlatform, SeverityTypes, SeyfertManager, SponsorBlockSegment, StateStorageType, StateTypes, Structure, TrackEndReasonTypes, TrackPartial, TrackSourceTypes, TrackUtils, UseNodeOptions };
|
|
3601
3660
|
export type { AlbumSearchResult, ArtistSearchResult, CPUStats, DiscordPacket, DistortionOptions, EndSpeakingEventVoiceReceiver, EndSpeakingEventVoiceReceiverData, EqualizerBand, ErrorOrEmptySearchResult, Exception, Extendable, FrameStats, IQueue, JsonConfig, KaraokeOptions, LavaPlayer, LavalinkInfo, LavalinkResponse, LoadType, Lyrics, LyricsEvent, LyricsEventType, LyricsFoundEvent, LyricsLine, LyricsLineEvent, LyricsNotFoundEvent, ManagerEvents, ManagerInitOptions, ManagerOptions, MemoryStats, NodeLinkGetLyrics, NodeLinkGetLyricsEmpty, NodeLinkGetLyricsError, NodeLinkGetLyricsMultiple, NodeLinkGetLyricsSingle, NodeMessage, NodeOptions, NodeStats, PlayOptions, PlayerEvent, PlayerEventType, PlayerEvents, PlayerOptions, PlayerStateUpdateEvent, PlayerUpdateVoiceState, PlaylistData, PlaylistInfoData, PlaylistRawData, PlaylistSearchResult, PodcastSearchResult, PortableUser, RedisConfig, RestPlayOptions, ReverbOptions, RotationOptions, SearchQuery, SearchResult, SearchSearchResult, Severity, ShortSearchResult, ShowSearchResult, Sizes, SponsorBlockChapterStarted, SponsorBlockChaptersLoaded, SponsorBlockSegmentEventType, SponsorBlockSegmentEvents, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, StartSpeakingEventVoiceReceiver, StartSpeakingEventVoiceReceiverData, StateStorageOptions, StationSearchResult, TimescaleOptions, Track, TrackData, TrackDataInfo, TrackEndEvent, TrackEndReason, TrackExceptionEvent, TrackPluginInfo, TrackSearchResult, TrackSourceName, TrackStartEvent, TrackStuckEvent, UseNodeOption, VibratoOptions, VoicePacket, VoiceReceiverEvent, VoiceServer, VoiceServerUpdate, VoiceState, WebSocketClosedEvent };
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ tslib_1.__exportStar(require("./structures/Plugin"), exports);
|
|
|
9
9
|
tslib_1.__exportStar(require("./statestorage/MemoryQueue"), exports);
|
|
10
10
|
tslib_1.__exportStar(require("./structures/Rest"), exports);
|
|
11
11
|
tslib_1.__exportStar(require("./structures/Utils"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./structures/MagmastreamError"), exports);
|
|
12
13
|
// wrappers
|
|
13
14
|
tslib_1.__exportStar(require("./wrappers/discord.js"), exports);
|
|
14
15
|
tslib_1.__exportStar(require("./wrappers/eris"), exports);
|
|
@@ -6,6 +6,7 @@ const Enums_1 = require("../structures/Enums");
|
|
|
6
6
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const Utils_1 = require("../structures/Utils");
|
|
9
|
+
const MagmastreamError_1 = require("../structures/MagmastreamError");
|
|
9
10
|
/**
|
|
10
11
|
* The player's queue, the `current` property is the currently playing track, think of the rest as the up-coming tracks.
|
|
11
12
|
*/
|
|
@@ -32,82 +33,106 @@ class JsonQueue {
|
|
|
32
33
|
* @param [offset=null] The position to add the track(s) at. If not provided, the track(s) will be added at the end of the queue.
|
|
33
34
|
*/
|
|
34
35
|
async add(track, offset) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
try {
|
|
37
|
+
const isArray = Array.isArray(track);
|
|
38
|
+
const inputTracks = isArray ? track : [track];
|
|
39
|
+
const tracks = [...inputTracks];
|
|
40
|
+
const queue = await this.getQueue();
|
|
41
|
+
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
42
|
+
// Set first track as current if none is active
|
|
43
|
+
if (!(await this.getCurrent())) {
|
|
44
|
+
const current = tracks.shift();
|
|
45
|
+
if (current) {
|
|
46
|
+
await this.setCurrent(current);
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
if (typeof offset === "number" && !isNaN(offset)) {
|
|
50
|
+
queue.splice(offset, 0, ...tracks);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
queue.push(...tracks);
|
|
54
|
+
}
|
|
55
|
+
await this.setQueue(queue);
|
|
56
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Added ${tracks.length} track(s) to queue`);
|
|
57
|
+
if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
|
|
58
|
+
if (!isArray) {
|
|
59
|
+
const AutoplayUser = (await this.manager.players.get(this.guildId).get("Internal_AutoplayUser"));
|
|
60
|
+
if (AutoplayUser && AutoplayUser.id === track.requester.id) {
|
|
61
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
62
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
63
|
+
details: {
|
|
64
|
+
type: "queue",
|
|
65
|
+
action: "autoPlayAdd",
|
|
66
|
+
tracks: [track],
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
72
|
}
|
|
73
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
74
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
75
|
+
details: {
|
|
76
|
+
type: "queue",
|
|
77
|
+
action: "add",
|
|
78
|
+
tracks,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
84
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
85
|
+
message: `Failed to add tracks to JSON queue for guild ${this.guildId}: ${err.message}`,
|
|
86
|
+
});
|
|
70
87
|
}
|
|
71
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
72
|
-
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
73
|
-
details: {
|
|
74
|
-
type: "queue",
|
|
75
|
-
action: "add",
|
|
76
|
-
tracks,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
88
|
}
|
|
80
89
|
/**
|
|
81
90
|
* @param track The track to add.
|
|
82
91
|
*/
|
|
83
92
|
async addPrevious(track) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
try {
|
|
94
|
+
const max = this.manager.options.maxPreviousTracks;
|
|
95
|
+
const tracks = Array.isArray(track) ? track : [track];
|
|
96
|
+
if (!tracks.length)
|
|
97
|
+
return;
|
|
98
|
+
const current = await this.getPrevious();
|
|
99
|
+
const newTracks = tracks.filter((t) => !current.some((p) => p.identifier === t.identifier));
|
|
100
|
+
if (!newTracks.length)
|
|
101
|
+
return;
|
|
102
|
+
const updated = [...current, ...newTracks];
|
|
103
|
+
const trimmed = updated.slice(-max);
|
|
104
|
+
await this.writeJSON(this.previousPath, trimmed);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
108
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
109
|
+
message: `Failed to add tracks to JSON queue for guild ${this.guildId}: ${err.message}`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
95
112
|
}
|
|
96
113
|
/**
|
|
97
114
|
* Clears the queue.
|
|
98
115
|
*/
|
|
99
116
|
async clear() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
try {
|
|
118
|
+
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
119
|
+
await this.deleteFile(this.queuePath);
|
|
120
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
121
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
122
|
+
details: {
|
|
123
|
+
type: "queue",
|
|
124
|
+
action: "clear",
|
|
125
|
+
tracks: [],
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Cleared the queue for: ${this.guildId}`);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
132
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
133
|
+
message: `Failed to clear JSON queue for guild ${this.guildId}: ${err.message}`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
111
136
|
}
|
|
112
137
|
/**
|
|
113
138
|
* Clears the previous tracks.
|
|
@@ -119,28 +144,52 @@ class JsonQueue {
|
|
|
119
144
|
* Removes the first track from the queue.
|
|
120
145
|
*/
|
|
121
146
|
async dequeue() {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
147
|
+
try {
|
|
148
|
+
const queue = await this.getQueue();
|
|
149
|
+
const track = queue.shift();
|
|
150
|
+
await this.setQueue(queue);
|
|
151
|
+
return track;
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
155
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
156
|
+
message: `Failed to dequeue track for guild ${this.guildId}: ${err.message}`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
126
159
|
}
|
|
127
160
|
/**
|
|
128
161
|
* @returns The total duration of the queue.
|
|
129
162
|
*/
|
|
130
163
|
async duration() {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
164
|
+
try {
|
|
165
|
+
const queue = await this.getQueue();
|
|
166
|
+
const current = await this.getCurrent();
|
|
167
|
+
const currentDuration = current?.duration || 0;
|
|
168
|
+
const total = queue.reduce((acc, track) => acc + (track.duration || 0), currentDuration);
|
|
169
|
+
return total;
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
173
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
174
|
+
message: `Failed to get duration for guild ${this.guildId}: ${err.message}`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
136
177
|
}
|
|
137
178
|
/**
|
|
138
179
|
* Adds a track to the front of the queue.
|
|
139
180
|
*/
|
|
140
181
|
async enqueueFront(track) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
182
|
+
try {
|
|
183
|
+
const tracks = Array.isArray(track) ? track : [track];
|
|
184
|
+
const queue = await this.getQueue();
|
|
185
|
+
await this.setQueue([...tracks.reverse(), ...queue]);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
189
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
190
|
+
message: `Failed to enqueue front track for guild ${this.guildId}: ${err.message}`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
144
193
|
}
|
|
145
194
|
/**
|
|
146
195
|
* Tests whether all elements in the queue pass the test implemented by the provided function.
|
|
@@ -211,76 +260,100 @@ class JsonQueue {
|
|
|
211
260
|
* @returns The newest track.
|
|
212
261
|
*/
|
|
213
262
|
async popPrevious() {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
263
|
+
try {
|
|
264
|
+
const current = await this.getPrevious();
|
|
265
|
+
if (!current.length)
|
|
266
|
+
return null;
|
|
267
|
+
const popped = current.pop();
|
|
268
|
+
await this.writeJSON(this.previousPath, current);
|
|
269
|
+
return popped;
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
273
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
274
|
+
message: `Failed to pop previous track for guild ${this.guildId}: ${err.message}`,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
220
277
|
}
|
|
221
278
|
async remove(startOrPos = 0, end) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
279
|
+
try {
|
|
280
|
+
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
281
|
+
const queue = await this.getQueue();
|
|
282
|
+
let removed = [];
|
|
283
|
+
if (typeof end === "number") {
|
|
284
|
+
if (startOrPos >= end || startOrPos >= queue.length)
|
|
285
|
+
throw new RangeError("Invalid range.");
|
|
286
|
+
removed = queue.splice(startOrPos, end - startOrPos);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
removed = queue.splice(startOrPos, 1);
|
|
290
|
+
}
|
|
291
|
+
await this.setQueue(queue);
|
|
292
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Removed ${removed.length} track(s) from position ${startOrPos}${end ? ` to ${end}` : ""}`);
|
|
293
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
294
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
295
|
+
details: {
|
|
296
|
+
type: "queue",
|
|
297
|
+
action: "remove",
|
|
298
|
+
tracks: removed,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
return removed;
|
|
229
302
|
}
|
|
230
|
-
|
|
231
|
-
|
|
303
|
+
catch (err) {
|
|
304
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
305
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
306
|
+
message: `Failed to remove track for guild ${this.guildId}: ${err.message}`,
|
|
307
|
+
});
|
|
232
308
|
}
|
|
233
|
-
await this.setQueue(queue);
|
|
234
|
-
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Removed ${removed.length} track(s) from position ${startOrPos}${end ? ` to ${end}` : ""}`);
|
|
235
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
236
|
-
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
237
|
-
details: {
|
|
238
|
-
type: "queue",
|
|
239
|
-
action: "remove",
|
|
240
|
-
tracks: removed,
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
return removed;
|
|
244
309
|
}
|
|
245
310
|
/**
|
|
246
311
|
* Shuffles the queue by round-robin.
|
|
247
312
|
*/
|
|
248
313
|
async roundRobinShuffle() {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
userMap.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// Shuffle each user's tracks
|
|
259
|
-
for (const tracks of userMap.values()) {
|
|
260
|
-
for (let i = tracks.length - 1; i > 0; i--) {
|
|
261
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
262
|
-
[tracks[i], tracks[j]] = [tracks[j], tracks[i]];
|
|
314
|
+
try {
|
|
315
|
+
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
316
|
+
const queue = await this.getQueue();
|
|
317
|
+
const userMap = new Map();
|
|
318
|
+
for (const track of queue) {
|
|
319
|
+
const userId = track.requester.id;
|
|
320
|
+
if (!userMap.has(userId))
|
|
321
|
+
userMap.set(userId, []);
|
|
322
|
+
userMap.get(userId).push(track);
|
|
263
323
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
324
|
+
// Shuffle each user's tracks
|
|
325
|
+
for (const tracks of userMap.values()) {
|
|
326
|
+
for (let i = tracks.length - 1; i > 0; i--) {
|
|
327
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
328
|
+
[tracks[i], tracks[j]] = [tracks[j], tracks[i]];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const users = [...userMap.keys()];
|
|
332
|
+
const queues = users.map((id) => userMap.get(id));
|
|
333
|
+
const shuffledQueue = [];
|
|
334
|
+
while (queues.some((q) => q.length > 0)) {
|
|
335
|
+
for (const q of queues) {
|
|
336
|
+
const track = q.shift();
|
|
337
|
+
if (track)
|
|
338
|
+
shuffledQueue.push(track);
|
|
339
|
+
}
|
|
273
340
|
}
|
|
341
|
+
await this.setQueue(shuffledQueue);
|
|
342
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
343
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
344
|
+
details: {
|
|
345
|
+
type: "queue",
|
|
346
|
+
action: "roundRobin",
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
353
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
354
|
+
message: `Failed to round robin shuffle queue for guild ${this.guildId}: ${err.message}`,
|
|
355
|
+
});
|
|
274
356
|
}
|
|
275
|
-
await this.setQueue(shuffledQueue);
|
|
276
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
277
|
-
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
278
|
-
details: {
|
|
279
|
-
type: "queue",
|
|
280
|
-
action: "roundRobin",
|
|
281
|
-
},
|
|
282
|
-
});
|
|
283
|
-
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
|
|
284
357
|
}
|
|
285
358
|
/**
|
|
286
359
|
* @param track The track to set.
|
|
@@ -306,21 +379,29 @@ class JsonQueue {
|
|
|
306
379
|
* Shuffles the queue.
|
|
307
380
|
*/
|
|
308
381
|
async shuffle() {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
382
|
+
try {
|
|
383
|
+
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
384
|
+
const queue = await this.getQueue();
|
|
385
|
+
for (let i = queue.length - 1; i > 0; i--) {
|
|
386
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
387
|
+
[queue[i], queue[j]] = [queue[j], queue[i]];
|
|
388
|
+
}
|
|
389
|
+
await this.setQueue(queue);
|
|
390
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
391
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
392
|
+
details: {
|
|
393
|
+
type: "queue",
|
|
394
|
+
action: "shuffle",
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Shuffled the queue for: ${this.guildId}`);
|
|
398
|
+
}
|
|
399
|
+
catch (err) {
|
|
400
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
401
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
402
|
+
message: `Failed to shuffle queue for guild ${this.guildId}: ${err.message}`,
|
|
403
|
+
});
|
|
314
404
|
}
|
|
315
|
-
await this.setQueue(queue);
|
|
316
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
317
|
-
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
318
|
-
details: {
|
|
319
|
-
type: "queue",
|
|
320
|
-
action: "shuffle",
|
|
321
|
-
},
|
|
322
|
-
});
|
|
323
|
-
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Shuffled the queue for: ${this.guildId}`);
|
|
324
405
|
}
|
|
325
406
|
/**
|
|
326
407
|
* @returns The size of the queue.
|
|
@@ -347,32 +428,40 @@ class JsonQueue {
|
|
|
347
428
|
* Shuffles the queue by user.
|
|
348
429
|
*/
|
|
349
430
|
async userBlockShuffle() {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
userMap.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const shuffledQueue = [];
|
|
360
|
-
while (shuffledQueue.length < queue.length) {
|
|
361
|
-
for (const [, tracks] of userMap) {
|
|
362
|
-
const track = tracks.shift();
|
|
363
|
-
if (track)
|
|
364
|
-
shuffledQueue.push(track);
|
|
431
|
+
try {
|
|
432
|
+
const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
|
|
433
|
+
const queue = await this.getQueue();
|
|
434
|
+
const userMap = new Map();
|
|
435
|
+
for (const track of queue) {
|
|
436
|
+
const userId = track.requester.id;
|
|
437
|
+
if (!userMap.has(userId))
|
|
438
|
+
userMap.set(userId, []);
|
|
439
|
+
userMap.get(userId).push(track);
|
|
365
440
|
}
|
|
441
|
+
const shuffledQueue = [];
|
|
442
|
+
while (shuffledQueue.length < queue.length) {
|
|
443
|
+
for (const [, tracks] of userMap) {
|
|
444
|
+
const track = tracks.shift();
|
|
445
|
+
if (track)
|
|
446
|
+
shuffledQueue.push(track);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
await this.setQueue(shuffledQueue);
|
|
450
|
+
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
451
|
+
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
452
|
+
details: {
|
|
453
|
+
type: "queue",
|
|
454
|
+
action: "userBlock",
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] userBlockShuffled the queue for: ${this.guildId}`);
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
461
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
462
|
+
message: `Failed to user block shuffle queue for guild ${this.guildId}: ${err.message}`,
|
|
463
|
+
});
|
|
366
464
|
}
|
|
367
|
-
await this.setQueue(shuffledQueue);
|
|
368
|
-
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
|
|
369
|
-
changeType: Enums_1.PlayerStateEventTypes.QueueChange,
|
|
370
|
-
details: {
|
|
371
|
-
type: "queue",
|
|
372
|
-
action: "userBlock",
|
|
373
|
-
},
|
|
374
|
-
});
|
|
375
|
-
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] userBlockShuffled the queue for: ${this.guildId}`);
|
|
376
465
|
}
|
|
377
466
|
// #endregion Public
|
|
378
467
|
// #region Private
|
|
@@ -391,6 +480,10 @@ class JsonQueue {
|
|
|
391
480
|
}
|
|
392
481
|
catch {
|
|
393
482
|
this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[JSONQUEUE] Failed to delete file: ${filePath}`);
|
|
483
|
+
throw new MagmastreamError_1.MagmaStreamError({
|
|
484
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
485
|
+
message: `Failed to delete file: ${filePath}`,
|
|
486
|
+
});
|
|
394
487
|
}
|
|
395
488
|
}
|
|
396
489
|
/**
|
|
@@ -427,7 +520,16 @@ class JsonQueue {
|
|
|
427
520
|
const raw = await fs_1.promises.readFile(filePath, "utf-8");
|
|
428
521
|
return JSON.parse(raw);
|
|
429
522
|
}
|
|
430
|
-
catch {
|
|
523
|
+
catch (err) {
|
|
524
|
+
const error = err instanceof MagmastreamError_1.MagmaStreamError
|
|
525
|
+
? err
|
|
526
|
+
: new MagmastreamError_1.MagmaStreamError({
|
|
527
|
+
code: Enums_1.MagmaStreamErrorCode.QUEUE_JSON_ERROR,
|
|
528
|
+
message: "An unknown error occurred.",
|
|
529
|
+
cause: err,
|
|
530
|
+
context: { stage: "SIGINT" },
|
|
531
|
+
});
|
|
532
|
+
console.error(error);
|
|
431
533
|
return null;
|
|
432
534
|
}
|
|
433
535
|
}
|