magmastream 2.9.0-dev.45 → 2.9.0-dev.47

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.
@@ -8,14 +8,14 @@ const Enums_1 = require("../structures/Enums");
8
8
  class RedisQueue {
9
9
  guildId;
10
10
  manager;
11
- /**
12
- * The Redis instance.
13
- */
14
- redis;
15
11
  /**
16
12
  * The prefix for the Redis keys.
17
13
  */
18
14
  redisPrefix;
15
+ /**
16
+ * The Redis instance.
17
+ */
18
+ redis;
19
19
  /**
20
20
  * Constructs a new RedisQueue.
21
21
  * @param guildId The guild ID.
@@ -29,97 +29,9 @@ class RedisQueue {
29
29
  ? manager.options.stateStorage.redisConfig.prefix
30
30
  : `${manager.options.stateStorage.redisConfig.prefix ?? "magmastream"}:`;
31
31
  }
32
+ // #region Public
32
33
  /**
33
- * @returns The queue key.
34
- */
35
- get queueKey() {
36
- return `${this.redisPrefix}queue:${this.guildId}:tracks`;
37
- }
38
- /**
39
- * @returns The current key.
40
- */
41
- get currentKey() {
42
- return `${this.redisPrefix}queue:${this.guildId}:current`;
43
- }
44
- /**
45
- * @returns The previous key.
46
- */
47
- get previousKey() {
48
- return `${this.redisPrefix}queue:${this.guildId}:previous`;
49
- }
50
- /**
51
- * Helper to serialize/deserialize Track
52
- */
53
- serialize(track) {
54
- return JSON.stringify(track);
55
- }
56
- /**
57
- * Helper to serialize/deserialize Track
58
- */
59
- deserialize(data) {
60
- return JSON.parse(data);
61
- }
62
- /**
63
- * @returns The current track.
64
- */
65
- async getCurrent() {
66
- const raw = await this.redis.get(this.currentKey);
67
- return raw ? this.deserialize(raw) : null;
68
- }
69
- /**
70
- * @param track The track to set.
71
- */
72
- async setCurrent(track) {
73
- if (track) {
74
- await this.redis.set(this.currentKey, this.serialize(track));
75
- }
76
- else {
77
- await this.redis.del(this.currentKey);
78
- }
79
- }
80
- /**
81
- * @returns The previous tracks.
82
- */
83
- async getPrevious() {
84
- const raw = await this.redis.lrange(this.previousKey, 0, -1);
85
- return raw.map(this.deserialize);
86
- }
87
- /**
88
- * @param track The track to add.
89
- */
90
- async addPrevious(track) {
91
- const tracks = Array.isArray(track) ? track : [track];
92
- if (!tracks.length)
93
- return;
94
- const serialized = tracks.map(this.serialize);
95
- if (!serialized.length)
96
- return;
97
- await this.redis.lpush(this.previousKey, ...serialized.reverse());
98
- }
99
- /**
100
- * @param track The track to set.
101
- */
102
- async setPrevious(track) {
103
- const tracks = Array.isArray(track) ? track : [track];
104
- if (!tracks.length)
105
- return;
106
- await this.redis.del(this.previousKey);
107
- await this.redis.rpush(this.previousKey, ...tracks.map(this.serialize));
108
- }
109
- /**
110
- * @returns The newest track.
111
- */
112
- async popPrevious() {
113
- const raw = await this.redis.lpop(this.previousKey); // get newest track (index 0)
114
- return raw ? this.deserialize(raw) : null;
115
- }
116
- /**
117
- * Clears the previous tracks.
118
- */
119
- async clearPrevious() {
120
- await this.redis.del(this.previousKey);
121
- }
122
- /**
34
+ * Adds a track or tracks to the queue.
123
35
  * @param track The track or tracks to add. Can be a single `Track` or an array of `Track`s.
124
36
  * @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.
125
37
  */
@@ -172,35 +84,18 @@ class RedisQueue {
172
84
  },
173
85
  });
174
86
  }
175
- async remove(startOrPos = 0, end) {
176
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
177
- const queue = await this.redis.lrange(this.queueKey, 0, -1);
178
- let removed = [];
179
- if (typeof end === "number") {
180
- if (startOrPos >= end || startOrPos >= queue.length) {
181
- throw new RangeError("Invalid range.");
182
- }
183
- removed = queue.slice(startOrPos, end);
184
- queue.splice(startOrPos, end - startOrPos);
185
- }
186
- else {
187
- removed = queue.splice(startOrPos, 1);
188
- }
189
- await this.redis.del(this.queueKey);
190
- if (queue.length > 0) {
191
- await this.redis.rpush(this.queueKey, ...queue);
192
- }
193
- const deserialized = removed.map(this.deserialize);
194
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Removed ${removed.length} track(s) from position ${startOrPos}${end ? ` to ${end}` : ""}`);
195
- this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
196
- changeType: Enums_1.PlayerStateEventTypes.QueueChange,
197
- details: {
198
- type: "queue",
199
- action: "remove",
200
- tracks: deserialized,
201
- },
202
- });
203
- return deserialized;
87
+ /**
88
+ * Adds a track or tracks to the previous tracks.
89
+ * @param track The track or tracks to add.
90
+ */
91
+ async addPrevious(track) {
92
+ const tracks = Array.isArray(track) ? track : [track];
93
+ if (!tracks.length)
94
+ return;
95
+ const serialized = tracks.map(this.serialize);
96
+ if (!serialized.length)
97
+ return;
98
+ await this.redis.lpush(this.previousKey, ...serialized.reverse());
204
99
  }
205
100
  /**
206
101
  * Clears the queue.
@@ -219,17 +114,17 @@ class RedisQueue {
219
114
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Cleared the queue for: ${this.guildId}`);
220
115
  }
221
116
  /**
222
- * @returns The size of the queue.
117
+ * Clears the previous tracks.
223
118
  */
224
- async size() {
225
- return await this.redis.llen(this.queueKey);
119
+ async clearPrevious() {
120
+ await this.redis.del(this.previousKey);
226
121
  }
227
122
  /**
228
- * @returns The total size of tracks in the queue including the current track.
123
+ * Removes the first track from the queue.
229
124
  */
230
- async totalSize() {
231
- const size = await this.size();
232
- return (await this.getCurrent()) ? size + 1 : size;
125
+ async dequeue() {
126
+ const raw = await this.redis.lpop(this.queueKey);
127
+ return raw ? this.deserialize(raw) : undefined;
233
128
  }
234
129
  /**
235
130
  * @returns The total duration of the queue in milliseconds.
@@ -250,60 +145,130 @@ class RedisQueue {
250
145
  return total;
251
146
  }
252
147
  /**
253
- * Shuffles the queue.
148
+ * Adds a track to the front of the queue.
149
+ * @param track The track or tracks to add.
254
150
  */
255
- async shuffle() {
256
- const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
151
+ async enqueueFront(track) {
152
+ const serialized = Array.isArray(track) ? track.map(this.serialize) : [this.serialize(track)];
153
+ // Redis: LPUSH adds to front, reverse to maintain order if multiple tracks
154
+ await this.redis.lpush(this.queueKey, ...serialized.reverse());
155
+ }
156
+ /**
157
+ * Whether all tracks in the queue match the specified condition.
158
+ * @param callback The condition to match.
159
+ * @returns Whether all tracks in the queue match the specified condition.
160
+ */
161
+ async everyAsync(callback) {
162
+ const tracks = await this.getTracks();
163
+ return tracks.every(callback);
164
+ }
165
+ /**
166
+ * Filters the tracks in the queue.
167
+ * @param callback The condition to match.
168
+ * @returns The tracks that match the condition.
169
+ */
170
+ async filterAsync(callback) {
171
+ const tracks = await this.getTracks();
172
+ return tracks.filter(callback);
173
+ }
174
+ /**
175
+ * Finds the first track in the queue that matches the specified condition.
176
+ * @param callback The condition to match.
177
+ * @returns The first track that matches the condition.
178
+ */
179
+ async findAsync(callback) {
180
+ const tracks = await this.getTracks();
181
+ return tracks.find(callback);
182
+ }
183
+ /**
184
+ * @returns The current track.
185
+ */
186
+ async getCurrent() {
187
+ const raw = await this.redis.get(this.currentKey);
188
+ return raw ? this.deserialize(raw) : null;
189
+ }
190
+ /**
191
+ * @returns The previous tracks.
192
+ */
193
+ async getPrevious() {
194
+ const raw = await this.redis.lrange(this.previousKey, 0, -1);
195
+ return raw.map(this.deserialize);
196
+ }
197
+ /**
198
+ * @returns The tracks in the queue from the start to the end.
199
+ */
200
+ async getSlice(start = 0, end = -1) {
201
+ const raw = await this.redis.lrange(this.queueKey, start, end === -1 ? -1 : end - 1);
202
+ return raw.map(this.deserialize);
203
+ }
204
+ /**
205
+ * @returns The tracks in the queue.
206
+ */
207
+ async getTracks() {
208
+ const raw = await this.redis.lrange(this.queueKey, 0, -1);
209
+ return raw.map(this.deserialize);
210
+ }
211
+ /**
212
+ * Maps the tracks in the queue.
213
+ * @returns The tracks in the queue after the specified index.
214
+ */
215
+ async mapAsync(callback) {
216
+ const tracks = await this.getTracks(); // same as lrange + deserialize
217
+ return tracks.map(callback);
218
+ }
219
+ /**
220
+ * Modifies the queue at the specified index.
221
+ * @param start The start index.
222
+ * @param deleteCount The number of tracks to delete.
223
+ * @param items The tracks to insert.
224
+ * @returns The removed tracks.
225
+ */
226
+ async modifyAt(start, deleteCount = 0, ...items) {
257
227
  const queue = await this.redis.lrange(this.queueKey, 0, -1);
258
- for (let i = queue.length - 1; i > 0; i--) {
259
- const j = Math.floor(Math.random() * (i + 1));
260
- [queue[i], queue[j]] = [queue[j], queue[i]];
261
- }
228
+ const removed = queue.splice(start, deleteCount, ...items.map(this.serialize));
262
229
  await this.redis.del(this.queueKey);
263
230
  if (queue.length > 0) {
264
231
  await this.redis.rpush(this.queueKey, ...queue);
265
232
  }
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: "shuffle",
271
- },
272
- });
273
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Shuffled the queue for: ${this.guildId}`);
233
+ return removed.map(this.deserialize);
274
234
  }
275
235
  /**
276
- * Shuffles the queue, but keeps the tracks of the same user together.
236
+ * Removes the newest track.
237
+ * @returns The newest track.
277
238
  */
278
- async userBlockShuffle() {
239
+ async popPrevious() {
240
+ const raw = await this.redis.lpop(this.previousKey); // get newest track (index 0)
241
+ return raw ? this.deserialize(raw) : null;
242
+ }
243
+ async remove(startOrPos = 0, end) {
279
244
  const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
280
- const rawTracks = await this.redis.lrange(this.queueKey, 0, -1);
281
- const deserialized = rawTracks.map(this.deserialize);
282
- const userMap = new Map();
283
- for (const track of deserialized) {
284
- const userId = track.requester.id;
285
- if (!userMap.has(userId))
286
- userMap.set(userId, []);
287
- userMap.get(userId).push(track);
288
- }
289
- const shuffledQueue = [];
290
- while (shuffledQueue.length < deserialized.length) {
291
- for (const [, tracks] of userMap) {
292
- const track = tracks.shift();
293
- if (track)
294
- shuffledQueue.push(track);
245
+ const queue = await this.redis.lrange(this.queueKey, 0, -1);
246
+ let removed = [];
247
+ if (typeof end === "number") {
248
+ if (startOrPos >= end || startOrPos >= queue.length) {
249
+ throw new RangeError("Invalid range.");
295
250
  }
251
+ removed = queue.slice(startOrPos, end);
252
+ queue.splice(startOrPos, end - startOrPos);
253
+ }
254
+ else {
255
+ removed = queue.splice(startOrPos, 1);
296
256
  }
297
257
  await this.redis.del(this.queueKey);
298
- await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
258
+ if (queue.length > 0) {
259
+ await this.redis.rpush(this.queueKey, ...queue);
260
+ }
261
+ const deserialized = removed.map(this.deserialize);
262
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Removed ${removed.length} track(s) from position ${startOrPos}${end ? ` to ${end}` : ""}`);
299
263
  this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
300
264
  changeType: Enums_1.PlayerStateEventTypes.QueueChange,
301
265
  details: {
302
266
  type: "queue",
303
- action: "userBlock",
267
+ action: "remove",
268
+ tracks: deserialized,
304
269
  },
305
270
  });
306
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] userBlockShuffled the queue for: ${this.guildId}`);
271
+ return deserialized;
307
272
  }
308
273
  /**
309
274
  * Shuffles the queue round-robin style.
@@ -348,80 +313,135 @@ class RedisQueue {
348
313
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] roundRobinShuffled the queue for: ${this.guildId}`);
349
314
  }
350
315
  /**
351
- * Removes the first track from the queue.
316
+ * Sets the current track.
317
+ * @param track The track to set.
352
318
  */
353
- async dequeue() {
354
- const raw = await this.redis.lpop(this.queueKey);
355
- return raw ? this.deserialize(raw) : undefined;
319
+ async setCurrent(track) {
320
+ if (track) {
321
+ await this.redis.set(this.currentKey, this.serialize(track));
322
+ }
323
+ else {
324
+ await this.redis.del(this.currentKey);
325
+ }
356
326
  }
357
327
  /**
358
- * Adds a track to the front of the queue.
328
+ * Sets the previous track(s).
329
+ * @param track The track to set.
359
330
  */
360
- async enqueueFront(track) {
361
- const serialized = Array.isArray(track) ? track.map(this.serialize) : [this.serialize(track)];
362
- // Redis: LPUSH adds to front, reverse to maintain order if multiple tracks
363
- await this.redis.lpush(this.queueKey, ...serialized.reverse());
331
+ async setPrevious(track) {
332
+ const tracks = Array.isArray(track) ? track : [track];
333
+ if (!tracks.length)
334
+ return;
335
+ await this.redis.del(this.previousKey);
336
+ await this.redis.rpush(this.previousKey, ...tracks.map(this.serialize));
364
337
  }
365
338
  /**
366
- * @returns The tracks in the queue.
339
+ * Shuffles the queue.
367
340
  */
368
- async getTracks() {
369
- const raw = await this.redis.lrange(this.queueKey, 0, -1);
370
- return raw.map(this.deserialize);
341
+ async shuffle() {
342
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
343
+ const queue = await this.redis.lrange(this.queueKey, 0, -1);
344
+ for (let i = queue.length - 1; i > 0; i--) {
345
+ const j = Math.floor(Math.random() * (i + 1));
346
+ [queue[i], queue[j]] = [queue[j], queue[i]];
347
+ }
348
+ await this.redis.del(this.queueKey);
349
+ if (queue.length > 0) {
350
+ await this.redis.rpush(this.queueKey, ...queue);
351
+ }
352
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
353
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
354
+ details: {
355
+ type: "queue",
356
+ action: "shuffle",
357
+ },
358
+ });
359
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] Shuffled the queue for: ${this.guildId}`);
371
360
  }
372
361
  /**
373
- * @returns The tracks in the queue from the start to the end.
362
+ * @returns The size of the queue.
374
363
  */
375
- async getSlice(start = 0, end = -1) {
376
- const raw = await this.redis.lrange(this.queueKey, start, end === -1 ? -1 : end - 1);
377
- return raw.map(this.deserialize);
364
+ async size() {
365
+ return await this.redis.llen(this.queueKey);
378
366
  }
379
367
  /**
380
- * Modifies the queue at the specified index.
368
+ * @returns Whether any tracks in the queue match the specified condition.
381
369
  */
382
- async modifyAt(start, deleteCount = 0, ...items) {
383
- const queue = await this.redis.lrange(this.queueKey, 0, -1);
384
- const removed = queue.splice(start, deleteCount, ...items.map(this.serialize));
385
- await this.redis.del(this.queueKey);
386
- if (queue.length > 0) {
387
- await this.redis.rpush(this.queueKey, ...queue);
370
+ async someAsync(callback) {
371
+ const tracks = await this.getTracks();
372
+ return tracks.some(callback);
373
+ }
374
+ /**
375
+ * @returns The total size of tracks in the queue including the current track.
376
+ */
377
+ async totalSize() {
378
+ const size = await this.size();
379
+ return (await this.getCurrent()) ? size + 1 : size;
380
+ }
381
+ /**
382
+ * Shuffles the queue, but keeps the tracks of the same user together.
383
+ */
384
+ async userBlockShuffle() {
385
+ const oldPlayer = this.manager.players.get(this.guildId) ? { ...this.manager.players.get(this.guildId) } : null;
386
+ const rawTracks = await this.redis.lrange(this.queueKey, 0, -1);
387
+ const deserialized = rawTracks.map(this.deserialize);
388
+ const userMap = new Map();
389
+ for (const track of deserialized) {
390
+ const userId = track.requester.id;
391
+ if (!userMap.has(userId))
392
+ userMap.set(userId, []);
393
+ userMap.get(userId).push(track);
388
394
  }
389
- return removed.map(this.deserialize);
395
+ const shuffledQueue = [];
396
+ while (shuffledQueue.length < deserialized.length) {
397
+ for (const [, tracks] of userMap) {
398
+ const track = tracks.shift();
399
+ if (track)
400
+ shuffledQueue.push(track);
401
+ }
402
+ }
403
+ await this.redis.del(this.queueKey);
404
+ await this.redis.rpush(this.queueKey, ...shuffledQueue.map(this.serialize));
405
+ this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, this.manager.players.get(this.guildId), {
406
+ changeType: Enums_1.PlayerStateEventTypes.QueueChange,
407
+ details: {
408
+ type: "queue",
409
+ action: "userBlock",
410
+ },
411
+ });
412
+ this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[QUEUE] userBlockShuffled the queue for: ${this.guildId}`);
390
413
  }
414
+ // #endregion Public
415
+ // #region Private
391
416
  /**
392
- * @returns The tracks in the queue after the specified index.
417
+ * @returns The current key.
393
418
  */
394
- async mapAsync(callback) {
395
- const tracks = await this.getTracks(); // same as lrange + deserialize
396
- return tracks.map(callback);
419
+ get currentKey() {
420
+ return `${this.redisPrefix}queue:${this.guildId}:current`;
397
421
  }
398
422
  /**
399
- * @returns The tracks in the queue that match the specified condition.
423
+ * Deserializes a track from a string.
400
424
  */
401
- async filterAsync(callback) {
402
- const tracks = await this.getTracks();
403
- return tracks.filter(callback);
425
+ deserialize(data) {
426
+ return JSON.parse(data);
404
427
  }
405
428
  /**
406
- * @returns The first track in the queue that matches the specified condition.
429
+ * @returns The previous key.
407
430
  */
408
- async findAsync(callback) {
409
- const tracks = await this.getTracks();
410
- return tracks.find(callback);
431
+ get previousKey() {
432
+ return `${this.redisPrefix}queue:${this.guildId}:previous`;
411
433
  }
412
434
  /**
413
- * @returns Whether any tracks in the queue match the specified condition.
435
+ * @returns The queue key.
414
436
  */
415
- async someAsync(callback) {
416
- const tracks = await this.getTracks();
417
- return tracks.some(callback);
437
+ get queueKey() {
438
+ return `${this.redisPrefix}queue:${this.guildId}:tracks`;
418
439
  }
419
440
  /**
420
- * @returns Whether all tracks in the queue match the specified condition.
441
+ * Helper to serialize/deserialize Track
421
442
  */
422
- async everyAsync(callback) {
423
- const tracks = await this.getTracks();
424
- return tracks.every(callback);
443
+ serialize(track) {
444
+ return JSON.stringify(track);
425
445
  }
426
446
  }
427
447
  exports.RedisQueue = RedisQueue;
@@ -89,6 +89,7 @@ var PlayerStateEventTypes;
89
89
  PlayerStateEventTypes["ChannelChange"] = "channelChange";
90
90
  PlayerStateEventTypes["PlayerCreate"] = "playerCreate";
91
91
  PlayerStateEventTypes["PlayerDestroy"] = "playerDestroy";
92
+ PlayerStateEventTypes["FilterChange"] = "filterChange";
92
93
  })(PlayerStateEventTypes || (exports.PlayerStateEventTypes = PlayerStateEventTypes = {}));
93
94
  /**
94
95
  * Track Source Types Enum