magmastream 2.9.0-dev.42 → 2.9.0-dev.44

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.
@@ -49,6 +49,7 @@ class Manager extends events_1.EventEmitter {
49
49
  // Initialize structures
50
50
  Utils_1.Structure.get("Player").init(this);
51
51
  Utils_1.TrackUtils.init(this);
52
+ Utils_1.PlayerUtils.init(this);
52
53
  if (options.trackPartial) {
53
54
  Utils_1.TrackUtils.setTrackPartial(options.trackPartial);
54
55
  delete options.trackPartial;
@@ -264,15 +265,6 @@ class Manager extends events_1.EventEmitter {
264
265
  getPlayer(guildId) {
265
266
  return this.players.get(guildId);
266
267
  }
267
- /**
268
- * @deprecated - Will be removed with v2.10.0 use {@link getPlayer} instead
269
- * Returns a player or undefined if it does not exist.
270
- * @param guildId The guild ID of the player to retrieve.
271
- * @returns The player if it exists, undefined otherwise.
272
- */
273
- async get(guildId) {
274
- return this.players.get(guildId);
275
- }
276
268
  /**
277
269
  * Creates a player or returns one if it already exists.
278
270
  * @param options The options to create the player with.
@@ -373,7 +365,7 @@ class Manager extends events_1.EventEmitter {
373
365
  * @returns A promise that resolves to an array of TrackData objects.
374
366
  * @throws Will throw an error if no nodes are available or if the API request fails.
375
367
  */
376
- decodeTracks(tracks) {
368
+ async decodeTracks(tracks) {
377
369
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Decoding tracks: ${JSON.stringify(tracks)}`);
378
370
  return new Promise(async (resolve, reject) => {
379
371
  const node = this.nodes.first();
@@ -407,18 +399,18 @@ class Manager extends events_1.EventEmitter {
407
399
  case Enums_1.StateStorageType.JSON:
408
400
  {
409
401
  try {
410
- const playerStateFilePath = await this.getPlayerFilePath(guildId);
402
+ const playerStateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
411
403
  const player = this.getPlayer(guildId);
412
404
  if (!player || player.state === Enums_1.StateTypes.Disconnected || !player.voiceChannelId) {
413
- console.warn(`[MANAGER] Skipping save for inactive player: ${guildId}`);
405
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Skipping save for inactive player: ${guildId}`);
414
406
  return;
415
407
  }
416
- const serializedPlayer = await this.serializePlayer(player);
408
+ const serializedPlayer = await Utils_1.PlayerUtils.serializePlayer(player);
417
409
  await promises_1.default.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
418
410
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Player state saved: ${guildId}`);
419
411
  }
420
412
  catch (error) {
421
- console.error(`[MANAGER] Error saving player state for guild ${guildId}:`, error);
413
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error saving player state for guild ${guildId}: ${error}`);
422
414
  }
423
415
  }
424
416
  break;
@@ -430,7 +422,7 @@ class Manager extends events_1.EventEmitter {
430
422
  console.warn(`[MANAGER] Skipping save for inactive player: ${guildId}`);
431
423
  return;
432
424
  }
433
- const serializedPlayer = await this.serializePlayer(player);
425
+ const serializedPlayer = await Utils_1.PlayerUtils.serializePlayer(player);
434
426
  const redisKey = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
435
427
  ? this.options.stateStorage.redisConfig.prefix
436
428
  : this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:${guildId}`;
@@ -438,7 +430,7 @@ class Manager extends events_1.EventEmitter {
438
430
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Player state saved to Redis: ${guildId}`);
439
431
  }
440
432
  catch (error) {
441
- console.error(`[MANAGER] Error saving player state to Redis for guild ${guildId}:`, error);
433
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error saving player state to Redis for guild ${guildId}: ${error}`);
442
434
  }
443
435
  }
444
436
  break;
@@ -468,196 +460,198 @@ class Manager extends events_1.EventEmitter {
468
460
  switch (this.options.stateStorage.type) {
469
461
  case Enums_1.StateStorageType.JSON:
470
462
  {
471
- const playerStatesDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
463
+ const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
472
464
  try {
473
- // Check if the directory exists, and create it if it doesn't
474
- await promises_1.default.access(playerStatesDir).catch(async () => {
475
- await promises_1.default.mkdir(playerStatesDir, { recursive: true });
476
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playerStatesDir}`);
465
+ // Ensure base players directory exists
466
+ await promises_1.default.access(playersBaseDir).catch(async () => {
467
+ await promises_1.default.mkdir(playersBaseDir, { recursive: true });
468
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playersBaseDir}`);
477
469
  });
478
- // Read the contents of the directory
479
- const playerFiles = await promises_1.default.readdir(playerStatesDir);
480
- // Process each file in the directory
481
- for (const file of playerFiles) {
482
- const filePath = path_1.default.join(playerStatesDir, file);
470
+ // Read guild directories inside players base dir
471
+ const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
472
+ for (const dirent of guildDirs) {
473
+ if (!dirent.isDirectory())
474
+ continue;
475
+ const guildId = dirent.name;
476
+ const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
483
477
  try {
484
- // Check if the file exists (though readdir should only return valid files)
485
- await promises_1.default.access(filePath); // Check if the file exists
486
- const data = await promises_1.default.readFile(filePath, "utf-8");
487
- const state = JSON.parse(data);
478
+ await promises_1.default.access(stateFilePath);
479
+ const rawData = await promises_1.default.readFile(stateFilePath, "utf-8");
480
+ const state = JSON.parse(rawData);
488
481
  if (state.clusterId !== this.options.clusterId)
489
482
  continue;
490
- if (state && typeof state === "object" && state.guildId && state.node.options.identifier === nodeId) {
491
- const lavaPlayer = info.find((player) => player.guildId === state.guildId);
492
- if (!lavaPlayer) {
493
- await this.destroy(state.guildId);
483
+ if (!state.guildId || state.node?.options?.identifier !== nodeId)
484
+ continue;
485
+ const lavaPlayer = info.find((player) => player.guildId === state.guildId);
486
+ if (!lavaPlayer) {
487
+ await this.destroy(state.guildId);
488
+ continue;
489
+ }
490
+ const playerOptions = {
491
+ guildId: state.options.guildId,
492
+ textChannelId: state.options.textChannelId,
493
+ voiceChannelId: state.options.voiceChannelId,
494
+ selfDeafen: state.options.selfDeafen,
495
+ volume: lavaPlayer.volume || state.options.volume,
496
+ nodeIdentifier: nodeId,
497
+ };
498
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
499
+ const player = this.create(playerOptions);
500
+ await player.node.rest.updatePlayer({
501
+ guildId: state.options.guildId,
502
+ data: {
503
+ voice: {
504
+ token: state.voiceState.event.token,
505
+ endpoint: state.voiceState.event.endpoint,
506
+ sessionId: state.voiceState.sessionId,
507
+ },
508
+ },
509
+ });
510
+ player.connect();
511
+ const tracks = [];
512
+ const currentTrack = state.queue.current;
513
+ const queueTracks = state.queue.tracks;
514
+ if (state.isAutoplay) {
515
+ Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
516
+ player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
517
+ }
518
+ if (lavaPlayer?.track) {
519
+ tracks.push(...queueTracks);
520
+ if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
521
+ await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
494
522
  }
495
- const playerOptions = {
496
- guildId: state.options.guildId,
497
- textChannelId: state.options.textChannelId,
498
- voiceChannelId: state.options.voiceChannelId,
499
- selfDeafen: state.options.selfDeafen,
500
- volume: lavaPlayer.volume || state.options.volume,
501
- nodeIdentifier: nodeId,
502
- };
503
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
504
- const player = this.create(playerOptions);
505
- await player.node.rest.updatePlayer({
506
- guildId: state.options.guildId,
507
- data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
508
- });
509
- player.connect();
510
- const tracks = [];
511
- const currentTrack = state.queue.current;
512
- const queueTracks = state.queue.tracks;
513
- if (state.isAutoplay) {
514
- Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
515
- player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
523
+ if (tracks.length > 0) {
524
+ await player.queue.clear();
525
+ await player.queue.add(tracks);
516
526
  }
517
- if (lavaPlayer?.track) {
518
- // If lavaPlayer has a track, push all queue tracks
519
- tracks.push(...queueTracks);
520
- // Set current track if matches lavaPlayer's track URI
521
- if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
522
- await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
523
- }
524
- // Add tracks to queue
525
- if (tracks.length > 0) {
527
+ }
528
+ else {
529
+ if (currentTrack) {
530
+ if (queueTracks.length > 0) {
531
+ tracks.push(...queueTracks);
526
532
  await player.queue.clear();
527
533
  await player.queue.add(tracks);
528
534
  }
535
+ await node.trackEnd(player, currentTrack, {
536
+ reason: Enums_1.TrackEndReasonTypes.Finished,
537
+ type: "TrackEndEvent",
538
+ });
529
539
  }
530
540
  else {
531
- // LavaPlayer missing track or lavaPlayer is falsy
532
- if (currentTrack) {
533
- if (queueTracks.length > 0) {
541
+ const previousQueue = await player.queue.getPrevious();
542
+ const lastTrack = previousQueue?.at(-1);
543
+ if (lastTrack) {
544
+ if (queueTracks.length === 0) {
545
+ await node.trackEnd(player, lastTrack, {
546
+ reason: Enums_1.TrackEndReasonTypes.Finished,
547
+ type: "TrackEndEvent",
548
+ });
549
+ }
550
+ else {
534
551
  tracks.push(...queueTracks);
552
+ if (tracks.length > 0) {
553
+ await player.queue.clear();
554
+ await player.queue.add(tracks);
555
+ }
556
+ }
557
+ }
558
+ else if (queueTracks.length > 0) {
559
+ tracks.push(...queueTracks);
560
+ if (tracks.length > 0) {
535
561
  await player.queue.clear();
536
562
  await player.queue.add(tracks);
537
563
  }
538
- await node.trackEnd(player, currentTrack, {
564
+ await node.trackEnd(player, lastTrack, {
539
565
  reason: Enums_1.TrackEndReasonTypes.Finished,
540
566
  type: "TrackEndEvent",
541
567
  });
542
568
  }
543
- else {
544
- // No current track, check previous queue for last track
545
- const previousQueue = await player.queue.getPrevious();
546
- const lastTrack = previousQueue?.at(-1);
547
- if (lastTrack) {
548
- if (queueTracks.length === 0) {
549
- // If no tracks in queue, end last track
550
- await node.trackEnd(player, lastTrack, {
551
- reason: Enums_1.TrackEndReasonTypes.Finished,
552
- type: "TrackEndEvent",
553
- });
554
- }
555
- else {
556
- // If there are queued tracks, add them
557
- tracks.push(...queueTracks);
558
- if (tracks.length > 0) {
559
- await player.queue.clear();
560
- await player.queue.add(tracks);
561
- }
562
- }
563
- }
564
- else {
565
- if (queueTracks.length > 0) {
566
- tracks.push(...queueTracks);
567
- if (tracks.length > 0) {
568
- await player.queue.clear();
569
- await player.queue.add(tracks);
570
- }
571
- await node.trackEnd(player, lastTrack, {
572
- reason: Enums_1.TrackEndReasonTypes.Finished,
573
- type: "TrackEndEvent",
574
- });
575
- }
576
- }
577
- }
578
- }
579
- if (state.queue.previous.length > 0) {
580
- await player.queue.addPrevious(state.queue.previous);
581
- }
582
- else {
583
- await player.queue.clearPrevious();
584
- }
585
- if (state.paused) {
586
- await player.pause(true);
587
- }
588
- else {
589
- player.paused = false;
590
- }
591
- if (state.trackRepeat)
592
- player.setTrackRepeat(true);
593
- if (state.queueRepeat)
594
- player.setQueueRepeat(true);
595
- if (state.dynamicRepeat) {
596
- player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
597
569
  }
598
- if (state.data) {
599
- for (const [name, value] of Object.entries(state.data)) {
600
- player.set(name, value);
601
- }
570
+ }
571
+ if (state.queue.previous.length > 0) {
572
+ await player.queue.addPrevious(state.queue.previous);
573
+ }
574
+ else {
575
+ await player.queue.clearPrevious();
576
+ }
577
+ if (state.paused) {
578
+ await player.pause(true);
579
+ }
580
+ else {
581
+ player.paused = false;
582
+ }
583
+ if (state.trackRepeat)
584
+ player.setTrackRepeat(true);
585
+ if (state.queueRepeat)
586
+ player.setQueueRepeat(true);
587
+ if (state.dynamicRepeat) {
588
+ player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
589
+ }
590
+ if (state.data) {
591
+ for (const [name, value] of Object.entries(state.data)) {
592
+ player.set(name, value);
602
593
  }
603
- const filterActions = {
604
- bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
605
- distort: (enabled) => player.filters.distort(enabled),
606
- setDistortion: () => player.filters.setDistortion(state.filters.distortion),
607
- eightD: (enabled) => player.filters.eightD(enabled),
608
- setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
609
- nightcore: (enabled) => player.filters.nightcore(enabled),
610
- slowmo: (enabled) => player.filters.slowmo(enabled),
611
- soft: (enabled) => player.filters.soft(enabled),
612
- trebleBass: (enabled) => player.filters.trebleBass(enabled),
613
- setTimescale: () => player.filters.setTimescale(state.filters.timescale),
614
- tv: (enabled) => player.filters.tv(enabled),
615
- vibrato: () => player.filters.setVibrato(state.filters.vibrato),
616
- vaporwave: (enabled) => player.filters.vaporwave(enabled),
617
- pop: (enabled) => player.filters.pop(enabled),
618
- party: (enabled) => player.filters.party(enabled),
619
- earrape: (enabled) => player.filters.earrape(enabled),
620
- electronic: (enabled) => player.filters.electronic(enabled),
621
- radio: (enabled) => player.filters.radio(enabled),
622
- setRotation: () => player.filters.setRotation(state.filters.rotation),
623
- tremolo: (enabled) => player.filters.tremolo(enabled),
624
- china: (enabled) => player.filters.china(enabled),
625
- chipmunk: (enabled) => player.filters.chipmunk(enabled),
626
- darthvader: (enabled) => player.filters.darthvader(enabled),
627
- daycore: (enabled) => player.filters.daycore(enabled),
628
- doubletime: (enabled) => player.filters.doubletime(enabled),
629
- demon: (enabled) => player.filters.demon(enabled),
630
- };
631
- // Iterate through filterStatus and apply the enabled filters
632
- for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
633
- if (isEnabled && filterActions[filter]) {
634
- filterActions[filter](true);
635
- }
594
+ }
595
+ const filterActions = {
596
+ bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
597
+ distort: (enabled) => player.filters.distort(enabled),
598
+ setDistortion: () => player.filters.setDistortion(state.filters.distortion),
599
+ eightD: (enabled) => player.filters.eightD(enabled),
600
+ setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
601
+ nightcore: (enabled) => player.filters.nightcore(enabled),
602
+ slowmo: (enabled) => player.filters.slowmo(enabled),
603
+ soft: (enabled) => player.filters.soft(enabled),
604
+ trebleBass: (enabled) => player.filters.trebleBass(enabled),
605
+ setTimescale: () => player.filters.setTimescale(state.filters.timescale),
606
+ tv: (enabled) => player.filters.tv(enabled),
607
+ vibrato: () => player.filters.setVibrato(state.filters.vibrato),
608
+ vaporwave: (enabled) => player.filters.vaporwave(enabled),
609
+ pop: (enabled) => player.filters.pop(enabled),
610
+ party: (enabled) => player.filters.party(enabled),
611
+ earrape: (enabled) => player.filters.earrape(enabled),
612
+ electronic: (enabled) => player.filters.electronic(enabled),
613
+ radio: (enabled) => player.filters.radio(enabled),
614
+ setRotation: () => player.filters.setRotation(state.filters.rotation),
615
+ tremolo: (enabled) => player.filters.tremolo(enabled),
616
+ china: (enabled) => player.filters.china(enabled),
617
+ chipmunk: (enabled) => player.filters.chipmunk(enabled),
618
+ darthvader: (enabled) => player.filters.darthvader(enabled),
619
+ daycore: (enabled) => player.filters.daycore(enabled),
620
+ doubletime: (enabled) => player.filters.doubletime(enabled),
621
+ demon: (enabled) => player.filters.demon(enabled),
622
+ };
623
+ for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
624
+ if (isEnabled && filterActions[filter]) {
625
+ filterActions[filter](true);
636
626
  }
637
- this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
638
- await this.sleep(1000);
639
627
  }
628
+ this.emit(Enums_1.ManagerEventTypes.PlayerRestored, player, node);
629
+ await this.sleep(1000);
640
630
  }
641
631
  catch (error) {
642
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing file ${filePath}: ${error}`);
643
- continue; // Skip to the next file if there's an error
632
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error processing player state for guild ${guildId}: ${error}`);
633
+ continue;
644
634
  }
645
635
  }
646
- // Delete all files inside playerStatesDir where nodeId matches
647
- for (const file of playerFiles) {
648
- const filePath = path_1.default.join(playerStatesDir, file);
636
+ // Cleanup old player state files from guild directories whose nodeId matches
637
+ for (const dirent of guildDirs) {
638
+ if (!dirent.isDirectory())
639
+ continue;
640
+ const guildId = dirent.name;
641
+ const stateFilePath = Utils_1.PlayerUtils.getPlayerStatePath(guildId);
649
642
  try {
650
- await promises_1.default.access(filePath); // Check if the file exists
651
- const data = await promises_1.default.readFile(filePath, "utf-8");
643
+ await promises_1.default.access(stateFilePath);
644
+ const data = await promises_1.default.readFile(stateFilePath, "utf-8");
652
645
  const state = JSON.parse(data);
653
- if (state && typeof state === "object" && state.node.options.identifier === nodeId) {
654
- await promises_1.default.unlink(filePath); // Delete the file asynchronously
655
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state file: ${filePath}`);
646
+ if (state && typeof state === "object" && state.node?.options?.identifier === nodeId) {
647
+ // Remove the entire guild directory or just the state file depending on your cleanup strategy
648
+ await promises_1.default.rm(Utils_1.PlayerUtils.getGuildDir(guildId), { recursive: true, force: true });
649
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted player state folder for guild ${guildId}`);
656
650
  }
657
651
  }
658
652
  catch (error) {
659
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error deleting file ${filePath}: ${error}`);
660
- continue; // Skip to the next file if there's an error
653
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error deleting player state for guild ${guildId}: ${error}`);
654
+ continue;
661
655
  }
662
656
  }
663
657
  }
@@ -1044,81 +1038,6 @@ class Manager extends events_1.EventEmitter {
1044
1038
  await player.pause(true);
1045
1039
  return;
1046
1040
  }
1047
- /**
1048
- * Gets each player's JSON file
1049
- * @param {string} guildId - The guild ID
1050
- * @returns {string} The path to the player's JSON file
1051
- */
1052
- async getPlayerFilePath(guildId) {
1053
- const configDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
1054
- try {
1055
- await promises_1.default.mkdir(configDir, { recursive: true });
1056
- return path_1.default.join(configDir, `${guildId}.json`);
1057
- }
1058
- catch (err) {
1059
- console.error(`[MANAGER] Error ensuring player data directory exists: ${err}`);
1060
- throw new Error(`Failed to resolve player file path for guild ${guildId}`);
1061
- }
1062
- }
1063
- /**
1064
- * Serializes a Player instance to avoid circular references.
1065
- * @param player The Player instance to serialize
1066
- * @returns The serialized Player instance
1067
- */
1068
- async serializePlayer(player) {
1069
- const seen = new WeakSet();
1070
- // Fetch async queue data once before serializing
1071
- const current = await player.queue.getCurrent();
1072
- const tracks = Array.isArray(await player.queue.getTracks()) ? await player.queue.getTracks() : [];
1073
- const previous = Array.isArray(await player.queue.getPrevious()) ? await player.queue.getPrevious() : [];
1074
- /**
1075
- * Recursively serializes an object, avoiding circular references.
1076
- * @param obj The object to serialize
1077
- * @returns The serialized object
1078
- */
1079
- const serialize = (obj) => {
1080
- if (obj && typeof obj === "object") {
1081
- if (seen.has(obj))
1082
- return;
1083
- seen.add(obj);
1084
- }
1085
- return obj;
1086
- };
1087
- return JSON.parse(JSON.stringify(player, (key, value) => {
1088
- if (key === "manager") {
1089
- return null;
1090
- }
1091
- if (key === "filters") {
1092
- if (!value || typeof value !== "object")
1093
- return null;
1094
- return {
1095
- distortion: value.distortion ?? null,
1096
- equalizer: value.equalizer ?? [],
1097
- karaoke: value.karaoke ?? null,
1098
- rotation: value.rotation ?? null,
1099
- timescale: value.timescale ?? null,
1100
- vibrato: value.vibrato ?? null,
1101
- reverb: value.reverb ?? null,
1102
- volume: value.volume ?? 1.0,
1103
- bassBoostlevel: value.bassBoostlevel ?? null,
1104
- filterStatus: value.filtersStatus ? { ...value.filtersStatus } : {},
1105
- };
1106
- }
1107
- if (key === "queue") {
1108
- return {
1109
- current,
1110
- tracks,
1111
- previous,
1112
- };
1113
- }
1114
- if (key === "data") {
1115
- return {
1116
- clientUser: value?.Internal_BotUser ?? null,
1117
- };
1118
- }
1119
- return serialize(value);
1120
- }));
1121
- }
1122
1041
  /**
1123
1042
  * Cleans up inactive players by removing their state files from the file system.
1124
1043
  * This is done to prevent stale state files from accumulating on the file system.
@@ -1127,30 +1046,18 @@ class Manager extends events_1.EventEmitter {
1127
1046
  switch (this.options.stateStorage.type) {
1128
1047
  case Enums_1.StateStorageType.JSON:
1129
1048
  {
1130
- const playerStoreDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
1131
- const playerDataDir = this.options.stateStorage?.jsonConfig?.path ?? path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
1049
+ const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
1132
1050
  try {
1133
- await promises_1.default.mkdir(playerStoreDir, { recursive: true });
1134
- await promises_1.default.mkdir(playerDataDir, { recursive: true });
1051
+ await promises_1.default.mkdir(playersBaseDir, { recursive: true });
1135
1052
  const activeGuildIds = new Set(this.players.keys());
1136
- // Clean up playerStore/*.json
1137
- const playerStateFiles = await promises_1.default.readdir(playerStoreDir);
1138
- for (const file of playerStateFiles) {
1139
- const guildId = path_1.default.basename(file, ".json");
1140
- if (!activeGuildIds.has(guildId)) {
1141
- const filePath = path_1.default.join(playerStoreDir, file);
1142
- await promises_1.default.unlink(filePath);
1143
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player state: ${guildId}`);
1144
- }
1145
- }
1146
- // Clean up players/<guildId>/ folders
1147
- const guildDirs = await promises_1.default.readdir(playerDataDir, { withFileTypes: true });
1053
+ // Cleanup inactive guild directories inside playersBaseDir
1054
+ const guildDirs = await promises_1.default.readdir(playersBaseDir, { withFileTypes: true });
1148
1055
  for (const dirent of guildDirs) {
1149
1056
  if (!dirent.isDirectory())
1150
1057
  continue;
1151
1058
  const guildId = dirent.name;
1152
1059
  if (!activeGuildIds.has(guildId)) {
1153
- const guildPath = path_1.default.join(playerDataDir, guildId);
1060
+ const guildPath = Utils_1.PlayerUtils.getGuildDir(guildId);
1154
1061
  await promises_1.default.rm(guildPath, { recursive: true, force: true });
1155
1062
  this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
1156
1063
  }
@@ -1202,13 +1109,11 @@ class Manager extends events_1.EventEmitter {
1202
1109
  switch (this.options.stateStorage.type) {
1203
1110
  case Enums_1.StateStorageType.JSON:
1204
1111
  {
1205
- const playersStoreDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
1206
- const playersDataDir = this.options.stateStorage?.jsonConfig?.path ?? path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
1207
1112
  try {
1208
1113
  if (!this.players.has(guildId)) {
1209
- await promises_1.default.unlink(path_1.default.join(playersStoreDir, `${guildId}.json`));
1210
- await promises_1.default.rm(path_1.default.join(playersDataDir, guildId), { recursive: true, force: true });
1211
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleting inactive player files: ${guildId}`);
1114
+ const guildDir = Utils_1.PlayerUtils.getGuildDir(guildId);
1115
+ await promises_1.default.rm(guildDir, { recursive: true, force: true });
1116
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Deleted inactive player data folder: ${guildId}`);
1212
1117
  }
1213
1118
  }
1214
1119
  catch (error) {
@@ -1283,19 +1188,18 @@ class Manager extends events_1.EventEmitter {
1283
1188
  switch (this.options.stateStorage.type) {
1284
1189
  case Enums_1.StateStorageType.Memory:
1285
1190
  case Enums_1.StateStorageType.JSON: {
1286
- const configDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "playerStore");
1191
+ const playersBaseDir = Utils_1.PlayerUtils.getPlayersBaseDir();
1287
1192
  try {
1288
- // Check if the directory exists, and create it if it doesn't
1289
- await promises_1.default.access(configDir).catch(async () => {
1290
- await promises_1.default.mkdir(configDir, { recursive: true });
1291
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Created directory: ${configDir}`);
1193
+ await promises_1.default.access(playersBaseDir).catch(async () => {
1194
+ await promises_1.default.mkdir(playersBaseDir, { recursive: true });
1195
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playersBaseDir}`);
1292
1196
  });
1293
- const files = await promises_1.default.readdir(configDir);
1294
- await Promise.all(files.map((file) => promises_1.default.unlink(path_1.default.join(configDir, file)).catch((err) => console.warn(`[MANAGER] Failed to delete file ${file}:`, err))));
1295
- this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Cleared all player state files in ${configDir}`);
1197
+ const files = await promises_1.default.readdir(playersBaseDir);
1198
+ await Promise.all(files.map((file) => promises_1.default.unlink(path_1.default.join(playersBaseDir, file)).catch((err) => this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Failed to delete file ${file}: ${err}`))));
1199
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Cleared all player state files in ${playersBaseDir}`);
1296
1200
  }
1297
1201
  catch (err) {
1298
- console.error("[MANAGER] Error clearing player state files:", err);
1202
+ this.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Error clearing player state files: ${err}`);
1299
1203
  }
1300
1204
  break;
1301
1205
  }
@@ -91,7 +91,7 @@ class Node {
91
91
  this.rest = new Rest_1.Rest(this, this.manager);
92
92
  switch (this.manager.options.stateStorage.type) {
93
93
  case Enums_1.StateStorageType.JSON:
94
- this.sessionIdsFilePath = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "sessionIds.json");
94
+ this.sessionIdsFilePath = path_1.default.join(process.cwd(), "magmastream", "sessionData", "sessionIds.json");
95
95
  const configDir = path_1.default.dirname(this.sessionIdsFilePath);
96
96
  if (!fs_1.default.existsSync(configDir)) {
97
97
  fs_1.default.mkdirSync(configDir, { recursive: true });
@@ -50,7 +50,7 @@ class Rest {
50
50
  }
51
51
  /**
52
52
  * Sends a PATCH request to update player related data.
53
- * @param {playOptions} options The options to update the player with.
53
+ * @param {RestPlayOptions} options The options to update the player with.
54
54
  * @returns {Promise<unknown>} Returns the result of the PATCH request.
55
55
  */
56
56
  async updatePlayer(options) {