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.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RedisQueue = void 0;
4
4
  const Enums_1 = require("../structures/Enums");
5
5
  const Utils_1 = require("../structures/Utils");
6
+ const MagmastreamError_1 = require("../structures/MagmastreamError");
6
7
  /**
7
8
  * The player's queue, the `current` property is the currently playing track, think of the rest as the up-coming tracks.
8
9
  */
@@ -26,9 +27,11 @@ class RedisQueue {
26
27
  this.guildId = guildId;
27
28
  this.manager = manager;
28
29
  this.redis = manager.redis;
29
- this.redisPrefix = manager.options.stateStorage.redisConfig.prefix?.endsWith(":")
30
- ? manager.options.stateStorage.redisConfig.prefix
31
- : `${manager.options.stateStorage.redisConfig.prefix ?? "magmastream"}:`;
30
+ const rawPrefix = manager.options.stateStorage.redisConfig.prefix;
31
+ let clean = typeof rawPrefix === "string" ? rawPrefix.trim() : "";
32
+ if (!clean.endsWith(":"))
33
+ clean = clean || "magmastream";
34
+ this.redisPrefix = `${clean}:`;
32
35
  }
33
36
  // #region Public
34
37
  /**
@@ -37,75 +40,126 @@ class RedisQueue {
37
40
  * @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.
38
41
  */
39
42
  async add(track, offset) {
40
- const isArray = Array.isArray(track);
41
- const tracks = isArray ? track : [track];
42
- const serialized = tracks.map((t) => this.serialize(t));
43
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
44
- // If there's no current track, pop one from the list
45
- if (!(await this.getCurrent())) {
46
- const current = serialized.shift();
47
- if (current) {
48
- await this.setCurrent(this.deserialize(current));
43
+ try {
44
+ const isArray = Array.isArray(track);
45
+ const tracks = isArray ? track : [track];
46
+ // Serialize tracks
47
+ const serialized = tracks.map((t) => this.serialize(t));
48
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
49
+ // Set current track if none exists
50
+ if (!(await this.getCurrent())) {
51
+ const current = serialized.shift();
52
+ if (current) {
53
+ await this.setCurrent(this.deserialize(current));
54
+ }
49
55
  }
50
- }
51
- if (typeof offset === "number" && !isNaN(offset)) {
52
- const queue = await this.redis.lrange(this.queueKey, 0, -1);
53
- queue.splice(offset, 0, ...serialized);
54
- await this.redis.del(this.queueKey);
55
- if (queue.length > 0) {
56
- await this.redis.rpush(this.queueKey, ...queue);
56
+ // Insert at offset or append
57
+ try {
58
+ if (typeof offset === "number" && !isNaN(offset)) {
59
+ const queue = await this.redis.lrange(this.queueKey, 0, -1);
60
+ queue.splice(offset, 0, ...serialized);
61
+ await this.redis.del(this.queueKey);
62
+ if (queue.length > 0) {
63
+ await this.redis.rpush(this.queueKey, ...queue);
64
+ }
65
+ }
66
+ else if (serialized.length > 0) {
67
+ await this.redis.rpush(this.queueKey, ...serialized);
68
+ }
57
69
  }
58
- }
59
- else if (serialized.length > 0) {
60
- await this.redis.rpush(this.queueKey, ...serialized);
61
- }
62
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Added ${tracks.length} track(s) to queue`);
63
- if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
64
- if (!Array.isArray(track)) {
65
- const AutoplayUser = (await this.manager.players.get(this.guildId).get("Internal_AutoplayUser"));
66
- if (AutoplayUser && AutoplayUser.id === track.requester.id) {
67
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
68
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
69
- details: {
70
- type: "queue",
71
- action: "autoPlayAdd",
72
- tracks: Array.isArray(track) ? track : [track],
73
- },
74
- });
75
- return;
70
+ catch (err) {
71
+ throw new MagmastreamError_1.MagmaStreamError({
72
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
73
+ message: `Failed to add tracks to Redis queue for guild ${this.guildId}: ${err.message}`,
74
+ });
75
+ }
76
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Added ${tracks.length} track(s) to queue`);
77
+ // Autoplay logic
78
+ if (this.manager.players.has(this.guildId) && this.manager.players.get(this.guildId).isAutoplay) {
79
+ if (!Array.isArray(track)) {
80
+ const AutoplayUser = (await this.manager.players.get(this.guildId).get("Internal_AutoplayUser"));
81
+ if (AutoplayUser && AutoplayUser.id === track.requester.id) {
82
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
83
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
84
+ details: {
85
+ type: "queue",
86
+ action: "autoPlayAdd",
87
+ tracks: [track],
88
+ },
89
+ });
90
+ return;
91
+ }
76
92
  }
77
93
  }
94
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
95
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
96
+ details: {
97
+ type: "queue",
98
+ action: "add",
99
+ tracks,
100
+ },
101
+ });
102
+ }
103
+ catch (err) {
104
+ if (err instanceof MagmastreamError_1.MagmaStreamError)
105
+ throw err;
106
+ throw new MagmastreamError_1.MagmaStreamError({
107
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
108
+ message: `Unexpected error in add() for guild ${this.guildId}: ${err.message}`,
109
+ });
78
110
  }
79
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
80
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
81
- details: {
82
- type: "queue",
83
- action: "add",
84
- tracks,
85
- },
86
- });
87
111
  }
88
112
  /**
89
113
  * Adds a track or tracks to the previous tracks.
90
114
  * @param track The track or tracks to add.
91
115
  */
92
116
  async addPrevious(track) {
93
- const tracks = Array.isArray(track) ? track : [track];
94
- if (!tracks.length)
95
- return;
96
- const serialized = tracks.map(this.serialize);
97
- if (!serialized.length)
98
- return;
99
- await this.redis.lpush(this.previousKey, ...serialized.reverse());
100
- const max = this.manager.options.maxPreviousTracks;
101
- await this.redis.ltrim(this.previousKey, 0, max - 1);
117
+ try {
118
+ const tracks = Array.isArray(track) ? track : [track];
119
+ if (!tracks.length) {
120
+ throw new MagmastreamError_1.MagmaStreamError({
121
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
122
+ message: `No tracks provided for addPrevious in guild ${this.guildId}`,
123
+ });
124
+ }
125
+ const serialized = tracks.map(this.serialize);
126
+ try {
127
+ // Push newest to TAIL
128
+ await this.redis.rpush(this.previousKey, ...serialized);
129
+ // Keep only the most recent maxPreviousTracks (trim from HEAD)
130
+ const max = this.manager.options.maxPreviousTracks;
131
+ await this.redis.ltrim(this.previousKey, -max, -1);
132
+ }
133
+ catch (err) {
134
+ throw new MagmastreamError_1.MagmaStreamError({
135
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
136
+ message: `Failed to add previous tracks to Redis for guild ${this.guildId}: ${err.message}`,
137
+ });
138
+ }
139
+ }
140
+ catch (err) {
141
+ if (err instanceof MagmastreamError_1.MagmaStreamError)
142
+ throw err;
143
+ throw new MagmastreamError_1.MagmaStreamError({
144
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
145
+ message: `Unexpected error in addPrevious() for guild ${this.guildId}: ${err.message}`,
146
+ });
147
+ }
102
148
  }
103
149
  /**
104
150
  * Clears the queue.
105
151
  */
106
152
  async clear() {
107
153
  const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
108
- await this.redis.del(this.queueKey);
154
+ try {
155
+ await this.redis.del(this.queueKey);
156
+ }
157
+ catch (err) {
158
+ throw new MagmastreamError_1.MagmaStreamError({
159
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
160
+ message: `Failed to clear queue for guild ${this.guildId}: ${err.message}`,
161
+ });
162
+ }
109
163
  this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
110
164
  changeType: Enums_1.PlayerStateEventTypes.QueueChange,
111
165
  details: {
@@ -120,41 +174,75 @@ class RedisQueue {
120
174
  * Clears the previous tracks.
121
175
  */
122
176
  async clearPrevious() {
123
- await this.redis.del(this.previousKey);
177
+ try {
178
+ await this.redis.del(this.previousKey);
179
+ }
180
+ catch (err) {
181
+ throw new MagmastreamError_1.MagmaStreamError({
182
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
183
+ message: `Failed to clear previous tracks for guild ${this.guildId}: ${err.message}`,
184
+ });
185
+ }
124
186
  }
125
187
  /**
126
188
  * Removes the first track from the queue.
127
189
  */
128
190
  async dequeue() {
129
- const raw = await this.redis.lpop(this.queueKey);
130
- return raw ? this.deserialize(raw) : undefined;
191
+ try {
192
+ const raw = await this.redis.lpop(this.queueKey);
193
+ return raw ? this.deserialize(raw) : undefined;
194
+ }
195
+ catch (err) {
196
+ throw new MagmastreamError_1.MagmaStreamError({
197
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
198
+ message: `Failed to dequeue track for guild ${this.guildId}: ${err.message}`,
199
+ });
200
+ }
131
201
  }
132
202
  /**
133
203
  * @returns The total duration of the queue in milliseconds.
134
204
  * This includes the duration of the currently playing track.
135
205
  */
136
206
  async duration() {
137
- const tracks = await this.redis.lrange(this.queueKey, 0, -1);
138
- const currentDuration = (await this.getCurrent())?.duration || 0;
139
- const total = tracks.reduce((acc, raw) => {
140
- try {
141
- const parsed = this.deserialize(raw);
142
- return acc + (parsed.duration || 0);
143
- }
144
- catch {
145
- return acc;
146
- }
147
- }, currentDuration);
148
- return total;
207
+ try {
208
+ const tracks = await this.redis.lrange(this.queueKey, 0, -1);
209
+ const currentDuration = (await this.getCurrent())?.duration || 0;
210
+ const total = tracks.reduce((acc, raw) => {
211
+ try {
212
+ const parsed = this.deserialize(raw);
213
+ return acc + (parsed.duration || 0);
214
+ }
215
+ catch (err) {
216
+ // Skip invalid tracks but log
217
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Skipping invalid track during duration calculation for guild ${this.guildId}: ${err.message}`);
218
+ return acc;
219
+ }
220
+ }, currentDuration);
221
+ return total;
222
+ }
223
+ catch (err) {
224
+ throw new MagmastreamError_1.MagmaStreamError({
225
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
226
+ message: `Failed to calculate total queue duration for guild ${this.guildId}: ${err.message}`,
227
+ });
228
+ }
149
229
  }
150
230
  /**
151
231
  * Adds a track to the front of the queue.
152
232
  * @param track The track or tracks to add.
153
233
  */
154
234
  async enqueueFront(track) {
155
- const serialized = Array.isArray(track) ? track.map(this.serialize) : [this.serialize(track)];
156
- // Redis: LPUSH adds to front, reverse to maintain order if multiple tracks
157
- await this.redis.lpush(this.queueKey, ...serialized.reverse());
235
+ try {
236
+ const serialized = Array.isArray(track) ? track.map(this.serialize) : [this.serialize(track)];
237
+ // Redis: LPUSH adds to front, reverse to maintain order if multiple tracks
238
+ await this.redis.lpush(this.queueKey, ...serialized.reverse());
239
+ }
240
+ catch (err) {
241
+ throw new MagmastreamError_1.MagmaStreamError({
242
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
243
+ message: `Failed to enqueue track to front for guild ${this.guildId}: ${err.message}`,
244
+ });
245
+ }
158
246
  }
159
247
  /**
160
248
  * Whether all tracks in the queue match the specified condition.
@@ -187,29 +275,61 @@ class RedisQueue {
187
275
  * @returns The current track.
188
276
  */
189
277
  async getCurrent() {
190
- const raw = await this.redis.get(this.currentKey);
191
- return raw ? this.deserialize(raw) : null;
278
+ try {
279
+ const raw = await this.redis.get(this.currentKey);
280
+ return raw ? this.deserialize(raw) : null;
281
+ }
282
+ catch (err) {
283
+ throw new MagmastreamError_1.MagmaStreamError({
284
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
285
+ message: `Failed to get current track for guild ${this.guildId}: ${err.message}`,
286
+ });
287
+ }
192
288
  }
193
289
  /**
194
290
  * @returns The previous tracks.
195
291
  */
196
292
  async getPrevious() {
197
- const raw = await this.redis.lrange(this.previousKey, 0, -1);
198
- return raw.map(this.deserialize);
293
+ try {
294
+ const raw = await this.redis.lrange(this.previousKey, 0, -1);
295
+ return raw.map(this.deserialize);
296
+ }
297
+ catch (err) {
298
+ throw new MagmastreamError_1.MagmaStreamError({
299
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
300
+ message: `Failed to get previous tracks for guild ${this.guildId}: ${err.message}`,
301
+ });
302
+ }
199
303
  }
200
304
  /**
201
305
  * @returns The tracks in the queue from the start to the end.
202
306
  */
203
307
  async getSlice(start = 0, end = -1) {
204
- const raw = await this.redis.lrange(this.queueKey, start, end === -1 ? -1 : end - 1);
205
- return raw.map(this.deserialize);
308
+ try {
309
+ const raw = await this.redis.lrange(this.queueKey, start, end === -1 ? -1 : end - 1);
310
+ return raw.map(this.deserialize);
311
+ }
312
+ catch (err) {
313
+ throw new MagmastreamError_1.MagmaStreamError({
314
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
315
+ message: `Failed to get slice of queue for guild ${this.guildId}: ${err.message}`,
316
+ });
317
+ }
206
318
  }
207
319
  /**
208
320
  * @returns The tracks in the queue.
209
321
  */
210
322
  async getTracks() {
211
- const raw = await this.redis.lrange(this.queueKey, 0, -1);
212
- return raw.map(this.deserialize);
323
+ try {
324
+ const raw = await this.redis.lrange(this.queueKey, 0, -1);
325
+ return raw.map(this.deserialize);
326
+ }
327
+ catch (err) {
328
+ throw new MagmastreamError_1.MagmaStreamError({
329
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
330
+ message: `Failed to get tracks for guild ${this.guildId}: ${err.message}`,
331
+ });
332
+ }
213
333
  }
214
334
  /**
215
335
  * Maps the tracks in the queue.
@@ -227,104 +347,145 @@ class RedisQueue {
227
347
  * @returns The removed tracks.
228
348
  */
229
349
  async modifyAt(start, deleteCount = 0, ...items) {
230
- const queue = await this.redis.lrange(this.queueKey, 0, -1);
231
- const removed = queue.splice(start, deleteCount, ...items.map(this.serialize));
232
- await this.redis.del(this.queueKey);
233
- if (queue.length > 0) {
234
- await this.redis.rpush(this.queueKey, ...queue);
350
+ try {
351
+ const queue = await this.redis.lrange(this.queueKey, 0, -1);
352
+ const removed = queue.splice(start, deleteCount, ...items.map(this.serialize));
353
+ await this.redis.del(this.queueKey);
354
+ if (queue.length > 0) {
355
+ await this.redis.rpush(this.queueKey, ...queue);
356
+ }
357
+ return removed.map(this.deserialize);
358
+ }
359
+ catch (err) {
360
+ throw new MagmastreamError_1.MagmaStreamError({
361
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
362
+ message: `Failed to modify queue at index ${start} for guild ${this.guildId}: ${err.message}`,
363
+ });
235
364
  }
236
- return removed.map(this.deserialize);
237
365
  }
238
366
  /**
239
367
  * Removes the newest track.
240
368
  * @returns The newest track.
241
369
  */
242
370
  async popPrevious() {
243
- const raw = await this.redis.lpop(this.previousKey); // get newest track (index 0)
244
- return raw ? this.deserialize(raw) : null;
371
+ try {
372
+ // Pop the newest track from the TAIL
373
+ const raw = await this.redis.rpop(this.previousKey);
374
+ return raw ? this.deserialize(raw) : null;
375
+ }
376
+ catch (err) {
377
+ throw new MagmastreamError_1.MagmaStreamError({
378
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
379
+ message: `Failed to pop previous track for guild ${this.guildId}: ${err.message}`,
380
+ });
381
+ }
245
382
  }
246
383
  async remove(startOrPos = 0, end) {
247
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
248
- const queue = await this.redis.lrange(this.queueKey, 0, -1);
249
- let removed = [];
250
- if (typeof end === "number") {
251
- if (startOrPos >= end || startOrPos >= queue.length) {
252
- throw new RangeError("Invalid range.");
384
+ try {
385
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
386
+ const queue = await this.redis.lrange(this.queueKey, 0, -1);
387
+ let removed = [];
388
+ if (typeof end === "number") {
389
+ if (startOrPos >= end || startOrPos >= queue.length) {
390
+ throw new RangeError("Invalid range.");
391
+ }
392
+ removed = queue.slice(startOrPos, end);
393
+ queue.splice(startOrPos, end - startOrPos);
253
394
  }
254
- removed = queue.slice(startOrPos, end);
255
- queue.splice(startOrPos, end - startOrPos);
256
- }
257
- else {
258
- removed = queue.splice(startOrPos, 1);
395
+ else {
396
+ removed = queue.splice(startOrPos, 1);
397
+ }
398
+ await this.redis.del(this.queueKey);
399
+ if (queue.length > 0) {
400
+ await this.redis.rpush(this.queueKey, ...queue);
401
+ }
402
+ const deserialized = removed.map(this.deserialize);
403
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Removed ${removed.length} track(s) from position ${startOrPos}${end ? ` to ${end}` : ""}`);
404
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
405
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
406
+ details: {
407
+ type: "queue",
408
+ action: "remove",
409
+ tracks: deserialized,
410
+ },
411
+ });
412
+ return deserialized;
259
413
  }
260
- await this.redis.del(this.queueKey);
261
- if (queue.length > 0) {
262
- await this.redis.rpush(this.queueKey, ...queue);
414
+ catch (error) {
415
+ throw new MagmastreamError_1.MagmaStreamError({
416
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
417
+ message: `Failed to remove track for guild ${this.guildId}: ${error.message}`,
418
+ });
263
419
  }
264
- const deserialized = removed.map(this.deserialize);
265
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Removed ${removed.length} track(s) from position ${startOrPos}${end ? ` to ${end}` : ""}`);
266
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
267
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
268
- details: {
269
- type: "queue",
270
- action: "remove",
271
- tracks: deserialized,
272
- },
273
- });
274
- return deserialized;
275
420
  }
276
421
  /**
277
422
  * Shuffles the queue round-robin style.
278
423
  */
279
424
  async roundRobinShuffle() {
280
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
281
- const rawTracks = await this.redis.lrange(this.queueKey, 0, -1);
282
- const deserialized = rawTracks.map(this.deserialize);
283
- const userMap = new Map();
284
- for (const track of deserialized) {
285
- const userId = track.requester.id;
286
- if (!userMap.has(userId))
287
- userMap.set(userId, []);
288
- userMap.get(userId).push(track);
289
- }
290
- // Shuffle each user's tracks
291
- for (const tracks of userMap.values()) {
292
- for (let i = tracks.length - 1; i > 0; i--) {
293
- const j = Math.floor(Math.random() * (i + 1));
294
- [tracks[i], tracks[j]] = [tracks[j], tracks[i]];
425
+ try {
426
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
427
+ const rawTracks = await this.redis.lrange(this.queueKey, 0, -1);
428
+ const deserialized = rawTracks.map(this.deserialize);
429
+ const userMap = new Map();
430
+ for (const track of deserialized) {
431
+ const userId = track.requester.id;
432
+ if (!userMap.has(userId))
433
+ userMap.set(userId, []);
434
+ userMap.get(userId).push(track);
295
435
  }
296
- }
297
- const users = [...userMap.keys()];
298
- const queues = users.map((id) => userMap.get(id));
299
- const shuffledQueue = [];
300
- while (queues.some((q) => q.length > 0)) {
301
- for (const q of queues) {
302
- const track = q.shift();
303
- if (track)
304
- shuffledQueue.push(track);
436
+ // Shuffle each user's tracks
437
+ for (const tracks of userMap.values()) {
438
+ for (let i = tracks.length - 1; i > 0; i--) {
439
+ const j = Math.floor(Math.random() * (i + 1));
440
+ [tracks[i], tracks[j]] = [tracks[j], tracks[i]];
441
+ }
442
+ }
443
+ const users = [...userMap.keys()];
444
+ const queues = users.map((id) => userMap.get(id));
445
+ const shuffledQueue = [];
446
+ while (queues.some((q) => q.length > 0)) {
447
+ for (const q of queues) {
448
+ const track = q.shift();
449
+ if (track)
450
+ shuffledQueue.push(track);
451
+ }
305
452
  }
453
+ await this.redis.del(this.queueKey);
454
+ await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
455
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
456
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
457
+ details: {
458
+ type: "queue",
459
+ action: "roundRobin",
460
+ },
461
+ });
462
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
463
+ }
464
+ catch (error) {
465
+ throw new MagmastreamError_1.MagmaStreamError({
466
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
467
+ message: `Failed to roundRobinShuffle the queue for guild ${this.guildId}: ${error.message}`,
468
+ });
306
469
  }
307
- await this.redis.del(this.queueKey);
308
- await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
309
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
310
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
311
- details: {
312
- type: "queue",
313
- action: "roundRobin",
314
- },
315
- });
316
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
317
470
  }
318
471
  /**
319
472
  * Sets the current track.
320
473
  * @param track The track to set.
321
474
  */
322
475
  async setCurrent(track) {
323
- if (track) {
324
- await this.redis.set(this.currentKey, this.serialize(track));
476
+ try {
477
+ if (track) {
478
+ await this.redis.set(this.currentKey, this.serialize(track));
479
+ }
480
+ else {
481
+ await this.redis.del(this.currentKey);
482
+ }
325
483
  }
326
- else {
327
- await this.redis.del(this.currentKey);
484
+ catch (error) {
485
+ throw new MagmastreamError_1.MagmaStreamError({
486
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
487
+ message: `Failed to setCurrent the queue for guild ${this.guildId}: ${error.message}`,
488
+ });
328
489
  }
329
490
  }
330
491
  /**
@@ -332,43 +493,67 @@ class RedisQueue {
332
493
  * @param track The track to set.
333
494
  */
334
495
  async setPrevious(track) {
335
- const tracks = Array.isArray(track) ? track : [track];
336
- if (!tracks.length)
337
- return;
338
- await this.redis
339
- .multi()
340
- .del(this.previousKey)
341
- .rpush(this.previousKey, ...tracks.map(this.serialize))
342
- .exec();
496
+ try {
497
+ const tracks = Array.isArray(track) ? track : [track];
498
+ if (!tracks.length)
499
+ return;
500
+ await this.redis
501
+ .multi()
502
+ .del(this.previousKey)
503
+ .rpush(this.previousKey, ...tracks.map(this.serialize))
504
+ .exec();
505
+ }
506
+ catch (error) {
507
+ throw new MagmastreamError_1.MagmaStreamError({
508
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
509
+ message: `Failed to setPrevious the queue for guild ${this.guildId}: ${error.message}`,
510
+ });
511
+ }
343
512
  }
344
513
  /**
345
514
  * Shuffles the queue.
346
515
  */
347
516
  async shuffle() {
348
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
349
- const queue = await this.redis.lrange(this.queueKey, 0, -1);
350
- for (let i = queue.length - 1; i > 0; i--) {
351
- const j = Math.floor(Math.random() * (i + 1));
352
- [queue[i], queue[j]] = [queue[j], queue[i]];
517
+ try {
518
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
519
+ const queue = await this.redis.lrange(this.queueKey, 0, -1);
520
+ for (let i = queue.length - 1; i > 0; i--) {
521
+ const j = Math.floor(Math.random() * (i + 1));
522
+ [queue[i], queue[j]] = [queue[j], queue[i]];
523
+ }
524
+ await this.redis.del(this.queueKey);
525
+ if (queue.length > 0) {
526
+ await this.redis.rpush(this.queueKey, ...queue);
527
+ }
528
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
529
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
530
+ details: {
531
+ type: "queue",
532
+ action: "shuffle",
533
+ },
534
+ });
535
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Shuffled the queue for: ${this.guildId}`);
353
536
  }
354
- await this.redis.del(this.queueKey);
355
- if (queue.length > 0) {
356
- await this.redis.rpush(this.queueKey, ...queue);
537
+ catch (error) {
538
+ throw new MagmastreamError_1.MagmaStreamError({
539
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
540
+ message: `Failed to shuffle the queue for guild ${this.guildId}: ${error.message}`,
541
+ });
357
542
  }
358
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
359
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
360
- details: {
361
- type: "queue",
362
- action: "shuffle",
363
- },
364
- });
365
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Shuffled the queue for: ${this.guildId}`);
366
543
  }
367
544
  /**
368
545
  * @returns The size of the queue.
369
546
  */
370
547
  async size() {
371
- return await this.redis.llen(this.queueKey);
548
+ try {
549
+ return await this.redis.llen(this.queueKey);
550
+ }
551
+ catch (error) {
552
+ throw new MagmastreamError_1.MagmaStreamError({
553
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
554
+ message: `Failed to get the size of the queue for guild ${this.guildId}: ${error.message}`,
555
+ });
556
+ }
372
557
  }
373
558
  /**
374
559
  * @returns Whether any tracks in the queue match the specified condition.
@@ -388,34 +573,42 @@ class RedisQueue {
388
573
  * Shuffles the queue, but keeps the tracks of the same user together.
389
574
  */
390
575
  async userBlockShuffle() {
391
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
392
- const rawTracks = await this.redis.lrange(this.queueKey, 0, -1);
393
- const deserialized = rawTracks.map(this.deserialize);
394
- const userMap = new Map();
395
- for (const track of deserialized) {
396
- const userId = track.requester.id;
397
- if (!userMap.has(userId))
398
- userMap.set(userId, []);
399
- userMap.get(userId).push(track);
400
- }
401
- const shuffledQueue = [];
402
- while (shuffledQueue.length < deserialized.length) {
403
- for (const [, tracks] of userMap) {
404
- const track = tracks.shift();
405
- if (track)
406
- shuffledQueue.push(track);
576
+ try {
577
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
578
+ const rawTracks = await this.redis.lrange(this.queueKey, 0, -1);
579
+ const deserialized = rawTracks.map(this.deserialize);
580
+ const userMap = new Map();
581
+ for (const track of deserialized) {
582
+ const userId = track.requester.id;
583
+ if (!userMap.has(userId))
584
+ userMap.set(userId, []);
585
+ userMap.get(userId).push(track);
407
586
  }
587
+ const shuffledQueue = [];
588
+ while (shuffledQueue.length < deserialized.length) {
589
+ for (const [, tracks] of userMap) {
590
+ const track = tracks.shift();
591
+ if (track)
592
+ shuffledQueue.push(track);
593
+ }
594
+ }
595
+ await this.redis.del(this.queueKey);
596
+ await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
597
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
598
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
599
+ details: {
600
+ type: "queue",
601
+ action: "userBlock",
602
+ },
603
+ });
604
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] userBlockShuffled the queue for: ${this.guildId}`);
605
+ }
606
+ catch (error) {
607
+ throw new MagmastreamError_1.MagmaStreamError({
608
+ code: Enums_1.MagmaStreamErrorCode.QUEUE_REDIS_ERROR,
609
+ message: `Failed to userBlockShuffle the queue for guild ${this.guildId}: ${error.message}`,
610
+ });
408
611
  }
409
- await this.redis.del(this.queueKey);
410
- await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
411
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
412
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
413
- details: {
414
- type: "queue",
415
- action: "userBlock",
416
- },
417
- });
418
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] userBlockShuffled the queue for: ${this.guildId}`);
419
612
  }
420
613
  // #endregion Public
421
614
  // #region Private