magmastream 2.9.0-dev.2 → 2.9.0-dev.21

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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ManagerEventTypes = exports.PlayerStateEventTypes = exports.SearchPlatform = exports.UseNodeOptions = exports.TrackPartial = exports.Manager = void 0;
3
+ exports.ManagerEventTypes = exports.PlayerStateEventTypes = exports.AutoPlayPlatform = exports.SearchPlatform = exports.UseNodeOptions = exports.TrackPartial = exports.StateStorageType = exports.Manager = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const Utils_1 = require("./Utils");
6
6
  const collection_1 = require("@discordjs/collection");
@@ -10,6 +10,7 @@ const managerCheck_1 = tslib_1.__importDefault(require("../utils/managerCheck"))
10
10
  const blockedWords_1 = require("../config/blockedWords");
11
11
  const promises_1 = tslib_1.__importDefault(require("fs/promises"));
12
12
  const path_1 = tslib_1.__importDefault(require("path"));
13
+ const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
13
14
  /**
14
15
  * The main hub for interacting with Lavalink and using Magmastream,
15
16
  */
@@ -21,13 +22,14 @@ class Manager extends events_1.EventEmitter {
21
22
  /** The options that were set. */
22
23
  options;
23
24
  initiated = false;
25
+ redis;
24
26
  /**
25
27
  * Initiates the Manager class.
26
28
  * @param options
27
29
  * @param options.enabledPlugins - An array of enabledPlugins to load.
28
30
  * @param options.nodes - An array of node options to create nodes from.
29
31
  * @param options.playNextOnEnd - Whether to automatically play the first track in the queue when the player is created.
30
- * @param options.autoPlaySearchPlatform - The search platform autoplay will use. Fallback to Youtube if not found.
32
+ * @param options.autoPlaySearchPlatforms - The search platform autoplay will use. Fallback to Youtube if not found.
31
33
  * @param options.enablePriorityMode - Whether to use the priority when selecting a node to play on.
32
34
  * @param options.clientName - The name of the client to send to Lavalink.
33
35
  * @param options.defaultSearchPlatform - The default search platform to use when searching for tracks.
@@ -61,9 +63,9 @@ class Manager extends events_1.EventEmitter {
61
63
  enablePriorityMode: false,
62
64
  clientName: "Magmastream",
63
65
  defaultSearchPlatform: SearchPlatform.YouTube,
64
- // autoPlaySearchPlatform: SearchPlatform.YouTube,
65
66
  useNode: UseNodeOptions.LeastPlayers,
66
67
  maxPreviousTracks: options.maxPreviousTracks ?? 20,
68
+ stateStorage: { type: StateStorageType.Collection },
67
69
  ...options,
68
70
  };
69
71
  if (this.options.nodes) {
@@ -119,7 +121,7 @@ class Manager extends events_1.EventEmitter {
119
121
  this.options.clusterId = clusterId;
120
122
  for (const node of this.nodes.values()) {
121
123
  try {
122
- node.connect(); // Connect the node
124
+ node.connect();
123
125
  }
124
126
  catch (err) {
125
127
  this.emit(ManagerEventTypes.NodeError, node, err);
@@ -132,6 +134,15 @@ class Manager extends events_1.EventEmitter {
132
134
  plugin.load(this);
133
135
  }
134
136
  }
137
+ if (this.options.stateStorage?.type === StateStorageType.Redis) {
138
+ const config = this.options.stateStorage.redisConfig;
139
+ this.redis = new ioredis_1.default({
140
+ host: config.host,
141
+ port: Number(config.port),
142
+ password: config.password,
143
+ db: config.db ?? 0,
144
+ });
145
+ }
135
146
  this.initiated = true;
136
147
  return this;
137
148
  }
@@ -199,6 +210,23 @@ class Manager extends events_1.EventEmitter {
199
210
  throw new Error(`An error occurred while searching: ${err}`);
200
211
  }
201
212
  }
213
+ /**
214
+ * Returns a player or undefined if it does not exist.
215
+ * @param guildId The guild ID of the player to retrieve.
216
+ * @returns The player if it exists, undefined otherwise.
217
+ */
218
+ getPlayer(guildId) {
219
+ return this.players.get(guildId);
220
+ }
221
+ /**
222
+ * @deprecated - Will be removed with v2.10.0 use {@link getPlayer} instead
223
+ * Returns a player or undefined if it does not exist.
224
+ * @param guildId The guild ID of the player to retrieve.
225
+ * @returns The player if it exists, undefined otherwise.
226
+ */
227
+ async get(guildId) {
228
+ return this.players.get(guildId);
229
+ }
202
230
  /**
203
231
  * Creates a player or returns one if it already exists.
204
232
  * @param options The options to create the player with.
@@ -212,14 +240,6 @@ class Manager extends events_1.EventEmitter {
212
240
  this.emit(ManagerEventTypes.Debug, `[MANAGER] Creating new player with options: ${JSON.stringify(options)}`);
213
241
  return new (Utils_1.Structure.get("Player"))(options);
214
242
  }
215
- /**
216
- * Returns a player or undefined if it does not exist.
217
- * @param guildId The guild ID of the player to retrieve.
218
- * @returns The player if it exists, undefined otherwise.
219
- */
220
- get(guildId) {
221
- return this.players.get(guildId);
222
- }
223
243
  /**
224
244
  * Destroys a player.
225
245
  * @param guildId The guild ID of the player to destroy.
@@ -284,7 +304,7 @@ class Manager extends events_1.EventEmitter {
284
304
  const update = "d" in data ? data.d : data;
285
305
  if (!this.isValidUpdate(update))
286
306
  return;
287
- const player = this.players.get(update.guild_id);
307
+ const player = this.getPlayer(update.guild_id);
288
308
  if (!player)
289
309
  return;
290
310
  this.emit(ManagerEventTypes.Debug, `[MANAGER] Updating voice state: ${JSON.stringify(update)}`);
@@ -293,8 +313,9 @@ class Manager extends events_1.EventEmitter {
293
313
  }
294
314
  if (update.user_id !== this.options.clientId)
295
315
  return;
296
- if (!player.voiceState.sessionId && player.voiceState.event) {
297
- if (player.state !== Utils_1.StateTypes.Disconnected) {
316
+ const missingSessionButHasEvent = !player.voiceState.sessionId && player.voiceState.event;
317
+ if (missingSessionButHasEvent) {
318
+ if (player.state !== Utils_1.StateTypes.Destroying && player.state !== Utils_1.StateTypes.Disconnected) {
298
319
  await player.destroy();
299
320
  }
300
321
  return;
@@ -337,21 +358,57 @@ class Manager extends events_1.EventEmitter {
337
358
  * @param {string} guildId - The guild ID of the player to save
338
359
  */
339
360
  async savePlayerState(guildId) {
340
- try {
341
- const playerStateFilePath = await this.getPlayerFilePath(guildId);
342
- const player = this.players.get(guildId);
343
- if (!player || player.state === Utils_1.StateTypes.Disconnected || !player.voiceChannelId) {
344
- console.warn(`Skipping save for inactive player: ${guildId}`);
361
+ switch (this.options.stateStorage.type) {
362
+ case StateStorageType.Collection:
363
+ {
364
+ try {
365
+ const playerStateFilePath = await this.getPlayerFilePath(guildId);
366
+ const player = this.getPlayer(guildId);
367
+ if (!player || player.state === Utils_1.StateTypes.Disconnected || !player.voiceChannelId) {
368
+ console.warn(`Skipping save for inactive player: ${guildId}`);
369
+ return;
370
+ }
371
+ const serializedPlayer = this.serializePlayer(player);
372
+ await promises_1.default.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
373
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Player state saved: ${guildId}`);
374
+ }
375
+ catch (error) {
376
+ console.error(`Error saving player state for guild ${guildId}:`, error);
377
+ }
378
+ }
379
+ break;
380
+ case StateStorageType.Redis:
381
+ {
382
+ try {
383
+ const player = this.getPlayer(guildId);
384
+ if (!player || player.state === Utils_1.StateTypes.Disconnected || !player.voiceChannelId) {
385
+ console.warn(`Skipping save for inactive player: ${guildId}`);
386
+ return;
387
+ }
388
+ const serializedPlayer = this.serializePlayer(player);
389
+ const redisKey = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
390
+ ? this.options.stateStorage.redisConfig.prefix
391
+ : this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:${guildId}`;
392
+ await this.redis.set(redisKey, JSON.stringify(serializedPlayer));
393
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Player state saved to Redis: ${guildId}`);
394
+ }
395
+ catch (error) {
396
+ console.error(`Error saving player state to Redis for guild ${guildId}:`, error);
397
+ }
398
+ }
399
+ break;
400
+ default:
345
401
  return;
346
- }
347
- const serializedPlayer = this.serializePlayer(player);
348
- await promises_1.default.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
349
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Player state saved: ${guildId}`);
350
- }
351
- catch (error) {
352
- console.error(`Error saving player state for guild ${guildId}:`, error);
353
402
  }
354
403
  }
404
+ /**
405
+ * Sleeps for a specified amount of time.
406
+ * @param ms The amount of time to sleep in milliseconds.
407
+ * @returns A promise that resolves after the specified amount of time.
408
+ */
409
+ async sleep(ms) {
410
+ return new Promise((resolve) => setTimeout(resolve, ms));
411
+ }
355
412
  /**
356
413
  * Loads player states from the JSON file.
357
414
  * @param nodeId The ID of the node to load player states from.
@@ -363,168 +420,332 @@ class Manager extends events_1.EventEmitter {
363
420
  if (!node)
364
421
  throw new Error(`Could not find node: ${nodeId}`);
365
422
  const info = (await node.rest.getAllPlayers());
366
- const playerStatesDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
367
- try {
368
- // Check if the directory exists, and create it if it doesn't
369
- await promises_1.default.access(playerStatesDir).catch(async () => {
370
- await promises_1.default.mkdir(playerStatesDir, { recursive: true });
371
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playerStatesDir}`);
372
- });
373
- // Read the contents of the directory
374
- const playerFiles = await promises_1.default.readdir(playerStatesDir);
375
- // Process each file in the directory
376
- for (const file of playerFiles) {
377
- const filePath = path_1.default.join(playerStatesDir, file);
378
- try {
379
- // Check if the file exists (though readdir should only return valid files)
380
- await promises_1.default.access(filePath);
381
- // Read the file asynchronously
382
- const data = await promises_1.default.readFile(filePath, "utf-8");
383
- const state = JSON.parse(data);
384
- if (state && typeof state === "object" && state.guildId && state.node.options.identifier === nodeId) {
385
- const lavaPlayer = info.find((player) => player.guildId === state.guildId);
386
- if (!lavaPlayer) {
387
- await this.destroy(state.guildId);
388
- }
389
- const playerOptions = {
390
- guildId: state.options.guildId,
391
- textChannelId: state.options.textChannelId,
392
- voiceChannelId: state.options.voiceChannelId,
393
- selfDeafen: state.options.selfDeafen,
394
- volume: lavaPlayer.volume || state.options.volume,
395
- };
396
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
397
- const player = this.create(playerOptions);
398
- await player.node.rest.updatePlayer({
399
- guildId: state.options.guildId,
400
- data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
423
+ switch (this.options.stateStorage.type) {
424
+ case StateStorageType.Collection:
425
+ {
426
+ const playerStatesDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
427
+ try {
428
+ // Check if the directory exists, and create it if it doesn't
429
+ await promises_1.default.access(playerStatesDir).catch(async () => {
430
+ await promises_1.default.mkdir(playerStatesDir, { recursive: true });
431
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playerStatesDir}`);
401
432
  });
402
- player.connect();
403
- const tracks = [];
404
- const currentTrack = state.queue.current;
405
- const queueTracks = state.queue.tracks;
406
- if (lavaPlayer) {
407
- if (lavaPlayer.track) {
408
- tracks.push(...queueTracks);
409
- if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
410
- player.queue.current = Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester);
411
- }
412
- }
413
- else {
414
- if (!currentTrack) {
415
- const payload = {
416
- reason: Utils_1.TrackEndReasonTypes.Finished,
433
+ // Read the contents of the directory
434
+ const playerFiles = await promises_1.default.readdir(playerStatesDir);
435
+ // Process each file in the directory
436
+ for (const file of playerFiles) {
437
+ const filePath = path_1.default.join(playerStatesDir, file);
438
+ try {
439
+ // Check if the file exists (though readdir should only return valid files)
440
+ await promises_1.default.access(filePath); // Check if the file exists
441
+ const data = await promises_1.default.readFile(filePath, "utf-8");
442
+ const state = JSON.parse(data);
443
+ if (state && typeof state === "object" && state.guildId && state.node.options.identifier === nodeId) {
444
+ const lavaPlayer = info.find((player) => player.guildId === state.guildId);
445
+ if (!lavaPlayer) {
446
+ await this.destroy(state.guildId);
447
+ }
448
+ const playerOptions = {
449
+ guildId: state.options.guildId,
450
+ textChannelId: state.options.textChannelId,
451
+ voiceChannelId: state.options.voiceChannelId,
452
+ selfDeafen: state.options.selfDeafen,
453
+ volume: lavaPlayer.volume || state.options.volume,
454
+ node: nodeId,
417
455
  };
418
- await node.queueEnd(player, currentTrack, payload);
419
- }
420
- else {
421
- tracks.push(currentTrack, ...queueTracks);
456
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
457
+ const player = this.create(playerOptions);
458
+ await player.node.rest.updatePlayer({
459
+ guildId: state.options.guildId,
460
+ data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
461
+ });
462
+ player.connect();
463
+ const tracks = [];
464
+ const currentTrack = state.queue.current;
465
+ const queueTracks = state.queue.tracks;
466
+ if (lavaPlayer) {
467
+ if (lavaPlayer.track) {
468
+ tracks.push(...queueTracks);
469
+ if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
470
+ await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
471
+ }
472
+ }
473
+ else {
474
+ if (!currentTrack) {
475
+ const payload = {
476
+ reason: Utils_1.TrackEndReasonTypes.Finished,
477
+ };
478
+ await node.queueEnd(player, currentTrack, payload);
479
+ }
480
+ else {
481
+ tracks.push(currentTrack, ...queueTracks);
482
+ }
483
+ }
484
+ }
485
+ else {
486
+ if (!currentTrack) {
487
+ const payload = {
488
+ reason: Utils_1.TrackEndReasonTypes.Finished,
489
+ };
490
+ await node.queueEnd(player, currentTrack, payload);
491
+ }
492
+ else {
493
+ tracks.push(currentTrack, ...queueTracks);
494
+ }
495
+ }
496
+ if (tracks.length > 0) {
497
+ await player.queue.add(tracks);
498
+ }
499
+ if (state.queue.previous.length > 0) {
500
+ await player.queue.addPrevious(state.queue.previous);
501
+ }
502
+ else {
503
+ await player.queue.clearPrevious();
504
+ }
505
+ if (state.paused) {
506
+ await player.pause(true);
507
+ }
508
+ else {
509
+ player.paused = false;
510
+ }
511
+ if (state.trackRepeat)
512
+ player.setTrackRepeat(true);
513
+ if (state.queueRepeat)
514
+ player.setQueueRepeat(true);
515
+ if (state.dynamicRepeat) {
516
+ player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
517
+ }
518
+ if (state.isAutoplay) {
519
+ Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
520
+ player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
521
+ }
522
+ if (state.data) {
523
+ for (const [name, value] of Object.entries(state.data)) {
524
+ player.set(name, value);
525
+ }
526
+ }
527
+ const filterActions = {
528
+ bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
529
+ distort: (enabled) => player.filters.distort(enabled),
530
+ setDistortion: () => player.filters.setDistortion(state.filters.distortion),
531
+ eightD: (enabled) => player.filters.eightD(enabled),
532
+ setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
533
+ nightcore: (enabled) => player.filters.nightcore(enabled),
534
+ slowmo: (enabled) => player.filters.slowmo(enabled),
535
+ soft: (enabled) => player.filters.soft(enabled),
536
+ trebleBass: (enabled) => player.filters.trebleBass(enabled),
537
+ setTimescale: () => player.filters.setTimescale(state.filters.timescale),
538
+ tv: (enabled) => player.filters.tv(enabled),
539
+ vibrato: () => player.filters.setVibrato(state.filters.vibrato),
540
+ vaporwave: (enabled) => player.filters.vaporwave(enabled),
541
+ pop: (enabled) => player.filters.pop(enabled),
542
+ party: (enabled) => player.filters.party(enabled),
543
+ earrape: (enabled) => player.filters.earrape(enabled),
544
+ electronic: (enabled) => player.filters.electronic(enabled),
545
+ radio: (enabled) => player.filters.radio(enabled),
546
+ setRotation: () => player.filters.setRotation(state.filters.rotation),
547
+ tremolo: (enabled) => player.filters.tremolo(enabled),
548
+ china: (enabled) => player.filters.china(enabled),
549
+ chipmunk: (enabled) => player.filters.chipmunk(enabled),
550
+ darthvader: (enabled) => player.filters.darthvader(enabled),
551
+ daycore: (enabled) => player.filters.daycore(enabled),
552
+ doubletime: (enabled) => player.filters.doubletime(enabled),
553
+ demon: (enabled) => player.filters.demon(enabled),
554
+ };
555
+ // Iterate through filterStatus and apply the enabled filters
556
+ for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
557
+ if (isEnabled && filterActions[filter]) {
558
+ filterActions[filter](true);
559
+ }
560
+ }
561
+ await this.sleep(1000);
422
562
  }
423
563
  }
564
+ catch (error) {
565
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error processing file ${filePath}: ${error}`);
566
+ continue; // Skip to the next file if there's an error
567
+ }
424
568
  }
425
- else {
426
- if (!currentTrack) {
427
- const payload = {
428
- reason: Utils_1.TrackEndReasonTypes.Finished,
429
- };
430
- await node.queueEnd(player, currentTrack, payload);
569
+ // Delete all files inside playerStatesDir where nodeId matches
570
+ for (const file of playerFiles) {
571
+ const filePath = path_1.default.join(playerStatesDir, file);
572
+ try {
573
+ await promises_1.default.access(filePath); // Check if the file exists
574
+ const data = await promises_1.default.readFile(filePath, "utf-8");
575
+ const state = JSON.parse(data);
576
+ if (state && typeof state === "object" && state.node.options.identifier === nodeId) {
577
+ await promises_1.default.unlink(filePath); // Delete the file asynchronously
578
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Deleted player state file: ${filePath}`);
579
+ }
431
580
  }
432
- else {
433
- tracks.push(currentTrack, ...queueTracks);
581
+ catch (error) {
582
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error deleting file ${filePath}: ${error}`);
583
+ continue; // Skip to the next file if there's an error
434
584
  }
435
585
  }
436
- if (tracks.length > 0) {
437
- player.queue.add(tracks);
438
- }
439
- if (state.queue.previous.length > 0) {
440
- player.queue.previous = state.queue.previous;
441
- }
442
- else {
443
- player.queue.previous = [];
444
- }
445
- if (state.paused) {
446
- await player.pause(true);
447
- }
448
- else {
449
- player.paused = false;
450
- }
451
- if (state.trackRepeat)
452
- player.setTrackRepeat(true);
453
- if (state.queueRepeat)
454
- player.setQueueRepeat(true);
455
- if (state.dynamicRepeat) {
456
- player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
457
- }
458
- if (state.isAutoplay) {
459
- Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
460
- player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
461
- }
462
- if (state.data) {
463
- for (const [name, value] of Object.entries(state.data)) {
464
- player.set(name, value);
586
+ }
587
+ catch (error) {
588
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
589
+ }
590
+ }
591
+ break;
592
+ case StateStorageType.Redis:
593
+ {
594
+ try {
595
+ // Get all keys matching our pattern
596
+ const redisKeyPattern = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
597
+ ? this.options.stateStorage.redisConfig.prefix
598
+ : this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:*`;
599
+ const keys = await this.redis.keys(redisKeyPattern);
600
+ for (const key of keys) {
601
+ try {
602
+ const data = await this.redis.get(key);
603
+ if (!data)
604
+ continue;
605
+ const state = JSON.parse(data);
606
+ if (!state || typeof state !== "object")
607
+ continue;
608
+ const guildId = key.split(":").pop();
609
+ if (!guildId)
610
+ continue;
611
+ if (state.node?.options?.identifier === nodeId) {
612
+ const lavaPlayer = info.find((player) => player.guildId === guildId);
613
+ if (!lavaPlayer) {
614
+ await this.destroy(guildId);
615
+ }
616
+ const playerOptions = {
617
+ guildId: state.options.guildId,
618
+ textChannelId: state.options.textChannelId,
619
+ voiceChannelId: state.options.voiceChannelId,
620
+ selfDeafen: state.options.selfDeafen,
621
+ volume: lavaPlayer?.volume || state.options.volume,
622
+ node: nodeId,
623
+ };
624
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
625
+ const player = this.create(playerOptions);
626
+ await player.node.rest.updatePlayer({
627
+ guildId: state.options.guildId,
628
+ data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
629
+ });
630
+ player.connect();
631
+ // Rest of the player state restoration code (tracks, filters, etc.)
632
+ const tracks = [];
633
+ const currentTrack = state.queue.current;
634
+ const queueTracks = state.queue.tracks;
635
+ if (lavaPlayer) {
636
+ if (lavaPlayer.track) {
637
+ tracks.push(...queueTracks);
638
+ if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
639
+ await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
640
+ }
641
+ }
642
+ else {
643
+ if (!currentTrack) {
644
+ const payload = {
645
+ reason: Utils_1.TrackEndReasonTypes.Finished,
646
+ };
647
+ await node.queueEnd(player, currentTrack, payload);
648
+ }
649
+ else {
650
+ tracks.push(currentTrack, ...queueTracks);
651
+ }
652
+ }
653
+ }
654
+ else {
655
+ if (!currentTrack) {
656
+ const payload = {
657
+ reason: Utils_1.TrackEndReasonTypes.Finished,
658
+ };
659
+ await node.queueEnd(player, currentTrack, payload);
660
+ }
661
+ else {
662
+ tracks.push(currentTrack, ...queueTracks);
663
+ }
664
+ }
665
+ if (tracks.length > 0) {
666
+ await player.queue.add(tracks);
667
+ }
668
+ if (state.queue.previous.length > 0) {
669
+ await player.queue.addPrevious(state.queue.previous);
670
+ }
671
+ else {
672
+ await player.queue.clearPrevious();
673
+ }
674
+ if (state.paused) {
675
+ await player.pause(true);
676
+ }
677
+ else {
678
+ player.paused = false;
679
+ }
680
+ if (state.trackRepeat)
681
+ player.setTrackRepeat(true);
682
+ if (state.queueRepeat)
683
+ player.setQueueRepeat(true);
684
+ if (state.dynamicRepeat) {
685
+ player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
686
+ }
687
+ if (state.isAutoplay) {
688
+ Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
689
+ player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
690
+ }
691
+ if (state.data) {
692
+ for (const [name, value] of Object.entries(state.data)) {
693
+ player.set(name, value);
694
+ }
695
+ }
696
+ const filterActions = {
697
+ bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
698
+ distort: (enabled) => player.filters.distort(enabled),
699
+ setDistortion: () => player.filters.setDistortion(state.filters.distortion),
700
+ eightD: (enabled) => player.filters.eightD(enabled),
701
+ setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
702
+ nightcore: (enabled) => player.filters.nightcore(enabled),
703
+ slowmo: (enabled) => player.filters.slowmo(enabled),
704
+ soft: (enabled) => player.filters.soft(enabled),
705
+ trebleBass: (enabled) => player.filters.trebleBass(enabled),
706
+ setTimescale: () => player.filters.setTimescale(state.filters.timescale),
707
+ tv: (enabled) => player.filters.tv(enabled),
708
+ vibrato: () => player.filters.setVibrato(state.filters.vibrato),
709
+ vaporwave: (enabled) => player.filters.vaporwave(enabled),
710
+ pop: (enabled) => player.filters.pop(enabled),
711
+ party: (enabled) => player.filters.party(enabled),
712
+ earrape: (enabled) => player.filters.earrape(enabled),
713
+ electronic: (enabled) => player.filters.electronic(enabled),
714
+ radio: (enabled) => player.filters.radio(enabled),
715
+ setRotation: () => player.filters.setRotation(state.filters.rotation),
716
+ tremolo: (enabled) => player.filters.tremolo(enabled),
717
+ china: (enabled) => player.filters.china(enabled),
718
+ chipmunk: (enabled) => player.filters.chipmunk(enabled),
719
+ darthvader: (enabled) => player.filters.darthvader(enabled),
720
+ daycore: (enabled) => player.filters.daycore(enabled),
721
+ doubletime: (enabled) => player.filters.doubletime(enabled),
722
+ demon: (enabled) => player.filters.demon(enabled),
723
+ };
724
+ // Iterate through filterStatus and apply the enabled filters
725
+ for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
726
+ if (isEnabled && filterActions[filter]) {
727
+ filterActions[filter](true);
728
+ }
729
+ }
730
+ // After processing, delete the Redis key
731
+ await this.redis.del(key);
732
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
733
+ await this.sleep(1000);
734
+ }
465
735
  }
466
- }
467
- const filterActions = {
468
- bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
469
- distort: (enabled) => player.filters.distort(enabled),
470
- setDistortion: () => player.filters.setDistortion(state.filters.distortion),
471
- eightD: (enabled) => player.filters.eightD(enabled),
472
- setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
473
- nightcore: (enabled) => player.filters.nightcore(enabled),
474
- slowmo: (enabled) => player.filters.slowmo(enabled),
475
- soft: (enabled) => player.filters.soft(enabled),
476
- trebleBass: (enabled) => player.filters.trebleBass(enabled),
477
- setTimescale: () => player.filters.setTimescale(state.filters.timescale),
478
- tv: (enabled) => player.filters.tv(enabled),
479
- vibrato: () => player.filters.setVibrato(state.filters.vibrato),
480
- vaporwave: (enabled) => player.filters.vaporwave(enabled),
481
- pop: (enabled) => player.filters.pop(enabled),
482
- party: (enabled) => player.filters.party(enabled),
483
- earrape: (enabled) => player.filters.earrape(enabled),
484
- electronic: (enabled) => player.filters.electronic(enabled),
485
- radio: (enabled) => player.filters.radio(enabled),
486
- setRotation: () => player.filters.setRotation(state.filters.rotation),
487
- tremolo: (enabled) => player.filters.tremolo(enabled),
488
- china: (enabled) => player.filters.china(enabled),
489
- chipmunk: (enabled) => player.filters.chipmunk(enabled),
490
- darthvader: (enabled) => player.filters.darthvader(enabled),
491
- daycore: (enabled) => player.filters.daycore(enabled),
492
- doubletime: (enabled) => player.filters.doubletime(enabled),
493
- demon: (enabled) => player.filters.demon(enabled),
494
- };
495
- // Iterate through filterStatus and apply the enabled filters
496
- for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
497
- if (isEnabled && filterActions[filter]) {
498
- filterActions[filter](true);
736
+ catch (error) {
737
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
738
+ continue;
499
739
  }
500
740
  }
501
741
  }
502
- }
503
- catch (error) {
504
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Error processing file ${filePath}: ${error}`);
505
- continue; // Skip to the next file if there's an error
506
- }
507
- }
508
- // Delete all files inside playerStatesDir where nodeId matches
509
- for (const file of playerFiles) {
510
- const filePath = path_1.default.join(playerStatesDir, file);
511
- try {
512
- await promises_1.default.access(filePath); // Check if the file exists
513
- const data = await promises_1.default.readFile(filePath, "utf-8");
514
- const state = JSON.parse(data);
515
- if (state && typeof state === "object" && state.node.options.identifier === nodeId) {
516
- await promises_1.default.unlink(filePath); // Delete the file asynchronously
517
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Deleted player state file: ${filePath}`);
742
+ catch (error) {
743
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
518
744
  }
519
745
  }
520
- catch (error) {
521
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Error deleting file ${filePath}: ${error}`);
522
- continue; // Skip to the next file if there's an error
523
- }
524
- }
525
- }
526
- catch (error) {
527
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
746
+ break;
747
+ default:
748
+ break;
528
749
  }
529
750
  this.emit(ManagerEventTypes.Debug, "[MANAGER] Finished loading saved players.");
530
751
  }
@@ -751,6 +972,8 @@ class Manager extends events_1.EventEmitter {
751
972
  return null;
752
973
  }
753
974
  if (key === "filters") {
975
+ if (!value || typeof value !== "object")
976
+ return null;
754
977
  return {
755
978
  distortion: value.distortion ?? null,
756
979
  equalizer: value.equalizer ?? [],
@@ -761,19 +984,19 @@ class Manager extends events_1.EventEmitter {
761
984
  reverb: value.reverb ?? null,
762
985
  volume: value.volume ?? 1.0,
763
986
  bassBoostlevel: value.bassBoostlevel ?? null,
764
- filterStatus: { ...value.filtersStatus },
987
+ filterStatus: value.filtersStatus ? { ...value.filtersStatus } : {},
765
988
  };
766
989
  }
767
990
  if (key === "queue") {
768
991
  return {
769
992
  current: value.current || null,
770
- tracks: [...value],
771
- previous: [...value.previous],
993
+ tracks: Array.isArray(value) ? [...value] : [],
994
+ previous: Array.isArray(value.previous) ? [...value.previous] : [],
772
995
  };
773
996
  }
774
997
  if (key === "data") {
775
998
  return {
776
- clientUser: value.Internal_BotUser ?? null,
999
+ clientUser: value?.Internal_BotUser ?? null,
777
1000
  };
778
1001
  }
779
1002
  return serialize(value);
@@ -873,6 +1096,11 @@ class Manager extends events_1.EventEmitter {
873
1096
  }
874
1097
  }
875
1098
  exports.Manager = Manager;
1099
+ var StateStorageType;
1100
+ (function (StateStorageType) {
1101
+ StateStorageType["Collection"] = "collection";
1102
+ StateStorageType["Redis"] = "redis";
1103
+ })(StateStorageType || (exports.StateStorageType = StateStorageType = {}));
876
1104
  var TrackPartial;
877
1105
  (function (TrackPartial) {
878
1106
  /** The base64 encoded string of the track */
@@ -917,6 +1145,7 @@ var SearchPlatform;
917
1145
  SearchPlatform["Bandcamp"] = "bcsearch";
918
1146
  SearchPlatform["Deezer"] = "dzsearch";
919
1147
  SearchPlatform["Jiosaavn"] = "jssearch";
1148
+ SearchPlatform["Qobuz"] = "qbsearch";
920
1149
  SearchPlatform["SoundCloud"] = "scsearch";
921
1150
  SearchPlatform["Spotify"] = "spsearch";
922
1151
  SearchPlatform["Tidal"] = "tdsearch";
@@ -924,6 +1153,16 @@ var SearchPlatform;
924
1153
  SearchPlatform["YouTube"] = "ytsearch";
925
1154
  SearchPlatform["YouTubeMusic"] = "ytmsearch";
926
1155
  })(SearchPlatform || (exports.SearchPlatform = SearchPlatform = {}));
1156
+ var AutoPlayPlatform;
1157
+ (function (AutoPlayPlatform) {
1158
+ AutoPlayPlatform["Spotify"] = "spotify";
1159
+ AutoPlayPlatform["Deezer"] = "deezer";
1160
+ AutoPlayPlatform["SoundCloud"] = "soundcloud";
1161
+ AutoPlayPlatform["Tidal"] = "tidal";
1162
+ AutoPlayPlatform["VKMusic"] = "vkmusic";
1163
+ AutoPlayPlatform["Qobuz"] = "qobuz";
1164
+ AutoPlayPlatform["YouTube"] = "youtube";
1165
+ })(AutoPlayPlatform || (exports.AutoPlayPlatform = AutoPlayPlatform = {}));
927
1166
  var PlayerStateEventTypes;
928
1167
  (function (PlayerStateEventTypes) {
929
1168
  PlayerStateEventTypes["AutoPlayChange"] = "playerAutoplay";
@@ -963,3 +1202,4 @@ var ManagerEventTypes;
963
1202
  ManagerEventTypes["ChapterStarted"] = "chapterStarted";
964
1203
  ManagerEventTypes["ChaptersLoaded"] = "chaptersLoaded";
965
1204
  })(ManagerEventTypes || (exports.ManagerEventTypes = ManagerEventTypes = {}));
1205
+ // PlayerStore WILL BE REMOVED IF YOU DONT FIND A USE FOR IT.