magmastream 2.9.0-dev.13 → 2.9.0-dev.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1055,6 +1055,12 @@ declare class Manager extends EventEmitter {
1055
1055
  * @param {string} guildId - The guild ID of the player to save
1056
1056
  */
1057
1057
  savePlayerState(guildId: string): Promise<void>;
1058
+ /**
1059
+ * Sleeps for a specified amount of time.
1060
+ * @param ms The amount of time to sleep in milliseconds.
1061
+ * @returns A promise that resolves after the specified amount of time.
1062
+ */
1063
+ private sleep;
1058
1064
  /**
1059
1065
  * Loads player states from the JSON file.
1060
1066
  * @param nodeId The ID of the node to load player states from.
@@ -1281,6 +1287,7 @@ declare enum SearchPlatform {
1281
1287
  Bandcamp = "bcsearch",
1282
1288
  Deezer = "dzsearch",
1283
1289
  Jiosaavn = "jssearch",
1290
+ Qobuz = "qbsearch",
1284
1291
  SoundCloud = "scsearch",
1285
1292
  Spotify = "spsearch",
1286
1293
  Tidal = "tdsearch",
@@ -1294,6 +1301,7 @@ declare enum AutoPlayPlatform {
1294
1301
  SoundCloud = "soundcloud",
1295
1302
  Tidal = "tidal",
1296
1303
  VKMusic = "vkmusic",
1304
+ Qobuz = "qobuz",
1297
1305
  YouTube = "youtube"
1298
1306
  }
1299
1307
  declare enum PlayerStateEventTypes {
@@ -357,21 +357,57 @@ class Manager extends events_1.EventEmitter {
357
357
  * @param {string} guildId - The guild ID of the player to save
358
358
  */
359
359
  async savePlayerState(guildId) {
360
- try {
361
- const playerStateFilePath = await this.getPlayerFilePath(guildId);
362
- const player = this.getPlayer(guildId);
363
- if (!player || player.state === Utils_1.StateTypes.Disconnected || !player.voiceChannelId) {
364
- console.warn(`Skipping save for inactive player: ${guildId}`);
360
+ switch (this.options.stateStorage.type) {
361
+ case StateStorageType.Collection:
362
+ {
363
+ try {
364
+ const playerStateFilePath = await this.getPlayerFilePath(guildId);
365
+ const player = this.getPlayer(guildId);
366
+ if (!player || player.state === Utils_1.StateTypes.Disconnected || !player.voiceChannelId) {
367
+ console.warn(`Skipping save for inactive player: ${guildId}`);
368
+ return;
369
+ }
370
+ const serializedPlayer = this.serializePlayer(player);
371
+ await promises_1.default.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
372
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Player state saved: ${guildId}`);
373
+ }
374
+ catch (error) {
375
+ console.error(`Error saving player state for guild ${guildId}:`, error);
376
+ }
377
+ }
378
+ break;
379
+ case StateStorageType.Redis:
380
+ {
381
+ try {
382
+ const player = this.getPlayer(guildId);
383
+ if (!player || player.state === Utils_1.StateTypes.Disconnected || !player.voiceChannelId) {
384
+ console.warn(`Skipping save for inactive player: ${guildId}`);
385
+ return;
386
+ }
387
+ const serializedPlayer = this.serializePlayer(player);
388
+ const redisKey = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
389
+ ? this.options.stateStorage.redisConfig.prefix
390
+ : this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:${guildId}`;
391
+ await this.redis.set(redisKey, JSON.stringify(serializedPlayer));
392
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Player state saved to Redis: ${guildId}`);
393
+ }
394
+ catch (error) {
395
+ console.error(`Error saving player state to Redis for guild ${guildId}:`, error);
396
+ }
397
+ }
398
+ break;
399
+ default:
365
400
  return;
366
- }
367
- const serializedPlayer = this.serializePlayer(player);
368
- await promises_1.default.writeFile(playerStateFilePath, JSON.stringify(serializedPlayer, null, 2), "utf-8");
369
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Player state saved: ${guildId}`);
370
- }
371
- catch (error) {
372
- console.error(`Error saving player state for guild ${guildId}:`, error);
373
401
  }
374
402
  }
403
+ /**
404
+ * Sleeps for a specified amount of time.
405
+ * @param ms The amount of time to sleep in milliseconds.
406
+ * @returns A promise that resolves after the specified amount of time.
407
+ */
408
+ async sleep(ms) {
409
+ return new Promise((resolve) => setTimeout(resolve, ms));
410
+ }
375
411
  /**
376
412
  * Loads player states from the JSON file.
377
413
  * @param nodeId The ID of the node to load player states from.
@@ -383,169 +419,332 @@ class Manager extends events_1.EventEmitter {
383
419
  if (!node)
384
420
  throw new Error(`Could not find node: ${nodeId}`);
385
421
  const info = (await node.rest.getAllPlayers());
386
- const playerStatesDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
387
- try {
388
- // Check if the directory exists, and create it if it doesn't
389
- await promises_1.default.access(playerStatesDir).catch(async () => {
390
- await promises_1.default.mkdir(playerStatesDir, { recursive: true });
391
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playerStatesDir}`);
392
- });
393
- // Read the contents of the directory
394
- const playerFiles = await promises_1.default.readdir(playerStatesDir);
395
- // Process each file in the directory
396
- for (const file of playerFiles) {
397
- const filePath = path_1.default.join(playerStatesDir, file);
398
- try {
399
- // Check if the file exists (though readdir should only return valid files)
400
- await promises_1.default.access(filePath);
401
- // Read the file asynchronously
402
- const data = await promises_1.default.readFile(filePath, "utf-8");
403
- const state = JSON.parse(data);
404
- if (state && typeof state === "object" && state.guildId && state.node.options.identifier === nodeId) {
405
- const lavaPlayer = info.find((player) => player.guildId === state.guildId);
406
- if (!lavaPlayer) {
407
- await this.destroy(state.guildId);
408
- }
409
- const playerOptions = {
410
- guildId: state.options.guildId,
411
- textChannelId: state.options.textChannelId,
412
- voiceChannelId: state.options.voiceChannelId,
413
- selfDeafen: state.options.selfDeafen,
414
- volume: lavaPlayer.volume || state.options.volume,
415
- node: nodeId,
416
- };
417
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
418
- const player = this.create(playerOptions);
419
- await player.node.rest.updatePlayer({
420
- guildId: state.options.guildId,
421
- data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
422
+ switch (this.options.stateStorage.type) {
423
+ case StateStorageType.Collection:
424
+ {
425
+ const playerStatesDir = path_1.default.join(process.cwd(), "magmastream", "dist", "sessionData", "players");
426
+ try {
427
+ // Check if the directory exists, and create it if it doesn't
428
+ await promises_1.default.access(playerStatesDir).catch(async () => {
429
+ await promises_1.default.mkdir(playerStatesDir, { recursive: true });
430
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Created directory: ${playerStatesDir}`);
422
431
  });
423
- player.connect();
424
- const tracks = [];
425
- const currentTrack = state.queue.current;
426
- const queueTracks = state.queue.tracks;
427
- if (lavaPlayer) {
428
- if (lavaPlayer.track) {
429
- tracks.push(...queueTracks);
430
- if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
431
- await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
432
- }
433
- }
434
- else {
435
- if (!currentTrack) {
436
- const payload = {
437
- reason: Utils_1.TrackEndReasonTypes.Finished,
432
+ // Read the contents of the directory
433
+ const playerFiles = await promises_1.default.readdir(playerStatesDir);
434
+ // Process each file in the directory
435
+ for (const file of playerFiles) {
436
+ const filePath = path_1.default.join(playerStatesDir, file);
437
+ try {
438
+ // Check if the file exists (though readdir should only return valid files)
439
+ await promises_1.default.access(filePath); // Check if the file exists
440
+ const data = await promises_1.default.readFile(filePath, "utf-8");
441
+ const state = JSON.parse(data);
442
+ if (state && typeof state === "object" && state.guildId && state.node.options.identifier === nodeId) {
443
+ const lavaPlayer = info.find((player) => player.guildId === state.guildId);
444
+ if (!lavaPlayer) {
445
+ await this.destroy(state.guildId);
446
+ }
447
+ const playerOptions = {
448
+ guildId: state.options.guildId,
449
+ textChannelId: state.options.textChannelId,
450
+ voiceChannelId: state.options.voiceChannelId,
451
+ selfDeafen: state.options.selfDeafen,
452
+ volume: lavaPlayer.volume || state.options.volume,
453
+ node: nodeId,
438
454
  };
439
- await node.queueEnd(player, currentTrack, payload);
440
- }
441
- else {
442
- tracks.push(currentTrack, ...queueTracks);
455
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${state.guildId} from saved file: ${JSON.stringify(state.options)}`);
456
+ const player = this.create(playerOptions);
457
+ await player.node.rest.updatePlayer({
458
+ guildId: state.options.guildId,
459
+ data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
460
+ });
461
+ player.connect();
462
+ const tracks = [];
463
+ const currentTrack = state.queue.current;
464
+ const queueTracks = state.queue.tracks;
465
+ if (lavaPlayer) {
466
+ if (lavaPlayer.track) {
467
+ tracks.push(...queueTracks);
468
+ if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
469
+ await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
470
+ }
471
+ }
472
+ else {
473
+ if (!currentTrack) {
474
+ const payload = {
475
+ reason: Utils_1.TrackEndReasonTypes.Finished,
476
+ };
477
+ await node.queueEnd(player, currentTrack, payload);
478
+ }
479
+ else {
480
+ tracks.push(currentTrack, ...queueTracks);
481
+ }
482
+ }
483
+ }
484
+ else {
485
+ if (!currentTrack) {
486
+ const payload = {
487
+ reason: Utils_1.TrackEndReasonTypes.Finished,
488
+ };
489
+ await node.queueEnd(player, currentTrack, payload);
490
+ }
491
+ else {
492
+ tracks.push(currentTrack, ...queueTracks);
493
+ }
494
+ }
495
+ if (tracks.length > 0) {
496
+ await player.queue.add(tracks);
497
+ }
498
+ if (state.queue.previous.length > 0) {
499
+ await player.queue.addPrevious(state.queue.previous);
500
+ }
501
+ else {
502
+ await player.queue.clearPrevious();
503
+ }
504
+ if (state.paused) {
505
+ await player.pause(true);
506
+ }
507
+ else {
508
+ player.paused = false;
509
+ }
510
+ if (state.trackRepeat)
511
+ player.setTrackRepeat(true);
512
+ if (state.queueRepeat)
513
+ player.setQueueRepeat(true);
514
+ if (state.dynamicRepeat) {
515
+ player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
516
+ }
517
+ if (state.isAutoplay) {
518
+ Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
519
+ player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
520
+ }
521
+ if (state.data) {
522
+ for (const [name, value] of Object.entries(state.data)) {
523
+ player.set(name, value);
524
+ }
525
+ }
526
+ const filterActions = {
527
+ bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
528
+ distort: (enabled) => player.filters.distort(enabled),
529
+ setDistortion: () => player.filters.setDistortion(state.filters.distortion),
530
+ eightD: (enabled) => player.filters.eightD(enabled),
531
+ setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
532
+ nightcore: (enabled) => player.filters.nightcore(enabled),
533
+ slowmo: (enabled) => player.filters.slowmo(enabled),
534
+ soft: (enabled) => player.filters.soft(enabled),
535
+ trebleBass: (enabled) => player.filters.trebleBass(enabled),
536
+ setTimescale: () => player.filters.setTimescale(state.filters.timescale),
537
+ tv: (enabled) => player.filters.tv(enabled),
538
+ vibrato: () => player.filters.setVibrato(state.filters.vibrato),
539
+ vaporwave: (enabled) => player.filters.vaporwave(enabled),
540
+ pop: (enabled) => player.filters.pop(enabled),
541
+ party: (enabled) => player.filters.party(enabled),
542
+ earrape: (enabled) => player.filters.earrape(enabled),
543
+ electronic: (enabled) => player.filters.electronic(enabled),
544
+ radio: (enabled) => player.filters.radio(enabled),
545
+ setRotation: () => player.filters.setRotation(state.filters.rotation),
546
+ tremolo: (enabled) => player.filters.tremolo(enabled),
547
+ china: (enabled) => player.filters.china(enabled),
548
+ chipmunk: (enabled) => player.filters.chipmunk(enabled),
549
+ darthvader: (enabled) => player.filters.darthvader(enabled),
550
+ daycore: (enabled) => player.filters.daycore(enabled),
551
+ doubletime: (enabled) => player.filters.doubletime(enabled),
552
+ demon: (enabled) => player.filters.demon(enabled),
553
+ };
554
+ // Iterate through filterStatus and apply the enabled filters
555
+ for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
556
+ if (isEnabled && filterActions[filter]) {
557
+ filterActions[filter](true);
558
+ }
559
+ }
560
+ await this.sleep(1000);
443
561
  }
444
562
  }
563
+ catch (error) {
564
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error processing file ${filePath}: ${error}`);
565
+ continue; // Skip to the next file if there's an error
566
+ }
445
567
  }
446
- else {
447
- if (!currentTrack) {
448
- const payload = {
449
- reason: Utils_1.TrackEndReasonTypes.Finished,
450
- };
451
- await node.queueEnd(player, currentTrack, payload);
568
+ // Delete all files inside playerStatesDir where nodeId matches
569
+ for (const file of playerFiles) {
570
+ const filePath = path_1.default.join(playerStatesDir, file);
571
+ try {
572
+ await promises_1.default.access(filePath); // Check if the file exists
573
+ const data = await promises_1.default.readFile(filePath, "utf-8");
574
+ const state = JSON.parse(data);
575
+ if (state && typeof state === "object" && state.node.options.identifier === nodeId) {
576
+ await promises_1.default.unlink(filePath); // Delete the file asynchronously
577
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Deleted player state file: ${filePath}`);
578
+ }
452
579
  }
453
- else {
454
- tracks.push(currentTrack, ...queueTracks);
580
+ catch (error) {
581
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error deleting file ${filePath}: ${error}`);
582
+ continue; // Skip to the next file if there's an error
455
583
  }
456
584
  }
457
- if (tracks.length > 0) {
458
- await player.queue.add(tracks);
459
- }
460
- if (state.queue.previous.length > 0) {
461
- await player.queue.addPrevious(state.queue.previous);
462
- }
463
- else {
464
- await player.queue.clearPrevious();
465
- }
466
- if (state.paused) {
467
- await player.pause(true);
468
- }
469
- else {
470
- player.paused = false;
471
- }
472
- if (state.trackRepeat)
473
- player.setTrackRepeat(true);
474
- if (state.queueRepeat)
475
- player.setQueueRepeat(true);
476
- if (state.dynamicRepeat) {
477
- player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
478
- }
479
- if (state.isAutoplay) {
480
- Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
481
- player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
482
- }
483
- if (state.data) {
484
- for (const [name, value] of Object.entries(state.data)) {
485
- player.set(name, value);
585
+ }
586
+ catch (error) {
587
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
588
+ }
589
+ }
590
+ break;
591
+ case StateStorageType.Redis:
592
+ {
593
+ try {
594
+ // Get all keys matching our pattern
595
+ const redisKeyPattern = `${this.options.stateStorage.redisConfig.prefix?.endsWith(":")
596
+ ? this.options.stateStorage.redisConfig.prefix
597
+ : this.options.stateStorage.redisConfig.prefix ?? "magmastream:"}playerstore:*`;
598
+ const keys = await this.redis.keys(redisKeyPattern);
599
+ for (const key of keys) {
600
+ try {
601
+ const data = await this.redis.get(key);
602
+ if (!data)
603
+ continue;
604
+ const state = JSON.parse(data);
605
+ if (!state || typeof state !== "object")
606
+ continue;
607
+ const guildId = key.split(":").pop();
608
+ if (!guildId)
609
+ continue;
610
+ if (state.node?.options?.identifier === nodeId) {
611
+ const lavaPlayer = info.find((player) => player.guildId === guildId);
612
+ if (!lavaPlayer) {
613
+ await this.destroy(guildId);
614
+ }
615
+ const playerOptions = {
616
+ guildId: state.options.guildId,
617
+ textChannelId: state.options.textChannelId,
618
+ voiceChannelId: state.options.voiceChannelId,
619
+ selfDeafen: state.options.selfDeafen,
620
+ volume: lavaPlayer?.volume || state.options.volume,
621
+ node: nodeId,
622
+ };
623
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Recreating player: ${guildId} from Redis`);
624
+ const player = this.create(playerOptions);
625
+ await player.node.rest.updatePlayer({
626
+ guildId: state.options.guildId,
627
+ data: { voice: { token: state.voiceState.event.token, endpoint: state.voiceState.event.endpoint, sessionId: state.voiceState.sessionId } },
628
+ });
629
+ player.connect();
630
+ // Rest of the player state restoration code (tracks, filters, etc.)
631
+ const tracks = [];
632
+ const currentTrack = state.queue.current;
633
+ const queueTracks = state.queue.tracks;
634
+ if (lavaPlayer) {
635
+ if (lavaPlayer.track) {
636
+ tracks.push(...queueTracks);
637
+ if (currentTrack && currentTrack.uri === lavaPlayer.track.info.uri) {
638
+ await player.queue.setCurrent(Utils_1.TrackUtils.build(lavaPlayer.track, currentTrack.requester));
639
+ }
640
+ }
641
+ else {
642
+ if (!currentTrack) {
643
+ const payload = {
644
+ reason: Utils_1.TrackEndReasonTypes.Finished,
645
+ };
646
+ await node.queueEnd(player, currentTrack, payload);
647
+ }
648
+ else {
649
+ tracks.push(currentTrack, ...queueTracks);
650
+ }
651
+ }
652
+ }
653
+ else {
654
+ if (!currentTrack) {
655
+ const payload = {
656
+ reason: Utils_1.TrackEndReasonTypes.Finished,
657
+ };
658
+ await node.queueEnd(player, currentTrack, payload);
659
+ }
660
+ else {
661
+ tracks.push(currentTrack, ...queueTracks);
662
+ }
663
+ }
664
+ if (tracks.length > 0) {
665
+ await player.queue.add(tracks);
666
+ }
667
+ if (state.queue.previous.length > 0) {
668
+ await player.queue.addPrevious(state.queue.previous);
669
+ }
670
+ else {
671
+ await player.queue.clearPrevious();
672
+ }
673
+ if (state.paused) {
674
+ await player.pause(true);
675
+ }
676
+ else {
677
+ player.paused = false;
678
+ }
679
+ if (state.trackRepeat)
680
+ player.setTrackRepeat(true);
681
+ if (state.queueRepeat)
682
+ player.setQueueRepeat(true);
683
+ if (state.dynamicRepeat) {
684
+ player.setDynamicRepeat(state.dynamicRepeat, state.dynamicLoopInterval._idleTimeout);
685
+ }
686
+ if (state.isAutoplay) {
687
+ Object.setPrototypeOf(state.data.clientUser, { constructor: { name: "User" } });
688
+ player.setAutoplay(true, state.data.clientUser, state.autoplayTries);
689
+ }
690
+ if (state.data) {
691
+ for (const [name, value] of Object.entries(state.data)) {
692
+ player.set(name, value);
693
+ }
694
+ }
695
+ const filterActions = {
696
+ bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
697
+ distort: (enabled) => player.filters.distort(enabled),
698
+ setDistortion: () => player.filters.setDistortion(state.filters.distortion),
699
+ eightD: (enabled) => player.filters.eightD(enabled),
700
+ setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
701
+ nightcore: (enabled) => player.filters.nightcore(enabled),
702
+ slowmo: (enabled) => player.filters.slowmo(enabled),
703
+ soft: (enabled) => player.filters.soft(enabled),
704
+ trebleBass: (enabled) => player.filters.trebleBass(enabled),
705
+ setTimescale: () => player.filters.setTimescale(state.filters.timescale),
706
+ tv: (enabled) => player.filters.tv(enabled),
707
+ vibrato: () => player.filters.setVibrato(state.filters.vibrato),
708
+ vaporwave: (enabled) => player.filters.vaporwave(enabled),
709
+ pop: (enabled) => player.filters.pop(enabled),
710
+ party: (enabled) => player.filters.party(enabled),
711
+ earrape: (enabled) => player.filters.earrape(enabled),
712
+ electronic: (enabled) => player.filters.electronic(enabled),
713
+ radio: (enabled) => player.filters.radio(enabled),
714
+ setRotation: () => player.filters.setRotation(state.filters.rotation),
715
+ tremolo: (enabled) => player.filters.tremolo(enabled),
716
+ china: (enabled) => player.filters.china(enabled),
717
+ chipmunk: (enabled) => player.filters.chipmunk(enabled),
718
+ darthvader: (enabled) => player.filters.darthvader(enabled),
719
+ daycore: (enabled) => player.filters.daycore(enabled),
720
+ doubletime: (enabled) => player.filters.doubletime(enabled),
721
+ demon: (enabled) => player.filters.demon(enabled),
722
+ };
723
+ // Iterate through filterStatus and apply the enabled filters
724
+ for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
725
+ if (isEnabled && filterActions[filter]) {
726
+ filterActions[filter](true);
727
+ }
728
+ }
729
+ // After processing, delete the Redis key
730
+ await this.redis.del(key);
731
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Deleted player state from Redis: ${key}`);
732
+ await this.sleep(1000);
733
+ }
486
734
  }
487
- }
488
- const filterActions = {
489
- bassboost: () => player.filters.bassBoost(state.filters.bassBoostlevel),
490
- distort: (enabled) => player.filters.distort(enabled),
491
- setDistortion: () => player.filters.setDistortion(state.filters.distortion),
492
- eightD: (enabled) => player.filters.eightD(enabled),
493
- setKaraoke: () => player.filters.setKaraoke(state.filters.karaoke),
494
- nightcore: (enabled) => player.filters.nightcore(enabled),
495
- slowmo: (enabled) => player.filters.slowmo(enabled),
496
- soft: (enabled) => player.filters.soft(enabled),
497
- trebleBass: (enabled) => player.filters.trebleBass(enabled),
498
- setTimescale: () => player.filters.setTimescale(state.filters.timescale),
499
- tv: (enabled) => player.filters.tv(enabled),
500
- vibrato: () => player.filters.setVibrato(state.filters.vibrato),
501
- vaporwave: (enabled) => player.filters.vaporwave(enabled),
502
- pop: (enabled) => player.filters.pop(enabled),
503
- party: (enabled) => player.filters.party(enabled),
504
- earrape: (enabled) => player.filters.earrape(enabled),
505
- electronic: (enabled) => player.filters.electronic(enabled),
506
- radio: (enabled) => player.filters.radio(enabled),
507
- setRotation: () => player.filters.setRotation(state.filters.rotation),
508
- tremolo: (enabled) => player.filters.tremolo(enabled),
509
- china: (enabled) => player.filters.china(enabled),
510
- chipmunk: (enabled) => player.filters.chipmunk(enabled),
511
- darthvader: (enabled) => player.filters.darthvader(enabled),
512
- daycore: (enabled) => player.filters.daycore(enabled),
513
- doubletime: (enabled) => player.filters.doubletime(enabled),
514
- demon: (enabled) => player.filters.demon(enabled),
515
- };
516
- // Iterate through filterStatus and apply the enabled filters
517
- for (const [filter, isEnabled] of Object.entries(state.filters.filterStatus)) {
518
- if (isEnabled && filterActions[filter]) {
519
- filterActions[filter](true);
735
+ catch (error) {
736
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error processing Redis key ${key}: ${error}`);
737
+ continue;
520
738
  }
521
739
  }
522
740
  }
523
- }
524
- catch (error) {
525
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Error processing file ${filePath}: ${error}`);
526
- continue; // Skip to the next file if there's an error
527
- }
528
- }
529
- // Delete all files inside playerStatesDir where nodeId matches
530
- for (const file of playerFiles) {
531
- const filePath = path_1.default.join(playerStatesDir, file);
532
- try {
533
- await promises_1.default.access(filePath); // Check if the file exists
534
- const data = await promises_1.default.readFile(filePath, "utf-8");
535
- const state = JSON.parse(data);
536
- if (state && typeof state === "object" && state.node.options.identifier === nodeId) {
537
- await promises_1.default.unlink(filePath); // Delete the file asynchronously
538
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Deleted player state file: ${filePath}`);
741
+ catch (error) {
742
+ this.emit(ManagerEventTypes.Debug, `[MANAGER] Error loading player states from Redis: ${error}`);
539
743
  }
540
744
  }
541
- catch (error) {
542
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Error deleting file ${filePath}: ${error}`);
543
- continue; // Skip to the next file if there's an error
544
- }
545
- }
546
- }
547
- catch (error) {
548
- this.emit(ManagerEventTypes.Debug, `[MANAGER] Error loading player states: ${error}`);
745
+ break;
746
+ default:
747
+ break;
549
748
  }
550
749
  this.emit(ManagerEventTypes.Debug, "[MANAGER] Finished loading saved players.");
551
750
  }
@@ -945,6 +1144,7 @@ var SearchPlatform;
945
1144
  SearchPlatform["Bandcamp"] = "bcsearch";
946
1145
  SearchPlatform["Deezer"] = "dzsearch";
947
1146
  SearchPlatform["Jiosaavn"] = "jssearch";
1147
+ SearchPlatform["Qobuz"] = "qbsearch";
948
1148
  SearchPlatform["SoundCloud"] = "scsearch";
949
1149
  SearchPlatform["Spotify"] = "spsearch";
950
1150
  SearchPlatform["Tidal"] = "tdsearch";
@@ -959,6 +1159,7 @@ var AutoPlayPlatform;
959
1159
  AutoPlayPlatform["SoundCloud"] = "soundcloud";
960
1160
  AutoPlayPlatform["Tidal"] = "tidal";
961
1161
  AutoPlayPlatform["VKMusic"] = "vkmusic";
1162
+ AutoPlayPlatform["Qobuz"] = "qobuz";
962
1163
  AutoPlayPlatform["YouTube"] = "youtube";
963
1164
  })(AutoPlayPlatform || (exports.AutoPlayPlatform = AutoPlayPlatform = {}));
964
1165
  var PlayerStateEventTypes;
@@ -350,7 +350,7 @@ class Node {
350
350
  this.manager.emit(Manager_1.ManagerEventTypes.Debug, `[NODE] Disconnected node: ${JSON.stringify(debugInfo)}`);
351
351
  // Try moving all players connected to that node to a useable one
352
352
  if (this.manager.useableNode) {
353
- const players = await this.manager.players.filter((p) => p.node.options.identifier == this.options.identifier);
353
+ const players = this.manager.players.filter((p) => p.node.options.identifier == this.options.identifier);
354
354
  if (players.size) {
355
355
  await Promise.all(Array.from(players.values(), (player) => player.autoMoveNode()));
356
356
  }
@@ -230,14 +230,13 @@ class Player {
230
230
  }
231
231
  await this.node.rest.destroyPlayer(this.guildId);
232
232
  await this.queue.clear();
233
+ await this.queue.clearPrevious();
234
+ await this.queue.setCurrent(null);
233
235
  this.manager.emit(Manager_1.ManagerEventTypes.PlayerStateUpdate, oldPlayer, null, {
234
236
  changeType: Manager_1.PlayerStateEventTypes.PlayerDestroy,
235
237
  });
236
238
  this.manager.emit(Manager_1.ManagerEventTypes.PlayerDestroy, this);
237
239
  const deleted = this.manager.players.delete(this.guildId);
238
- if (!deleted) {
239
- console.warn(`Failed to delete player with guildId: ${this.guildId}`);
240
- }
241
240
  return deleted;
242
241
  }
243
242
  /**
@@ -603,6 +603,62 @@ class AutoPlayUtils {
603
603
  return result.tracks;
604
604
  }
605
605
  break;
606
+ case "qobuz":
607
+ {
608
+ if (!track.uri.includes("qobuz.com")) {
609
+ const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.Qobuz }, track.requester);
610
+ if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
611
+ return [];
612
+ }
613
+ if (res.loadType === LoadTypes.Playlist) {
614
+ res.tracks = res.playlist.tracks;
615
+ }
616
+ if (!res.tracks.length) {
617
+ return [];
618
+ }
619
+ track = res.tracks[0];
620
+ }
621
+ const identifier = `qbrec:${track.identifier}`;
622
+ const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
623
+ if (!recommendedResult) {
624
+ return [];
625
+ }
626
+ let tracks = [];
627
+ let playlist = null;
628
+ const requester = track.requester;
629
+ switch (recommendedResult.loadType) {
630
+ case LoadTypes.Search:
631
+ tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
632
+ break;
633
+ case LoadTypes.Track:
634
+ tracks = [TrackUtils.build(recommendedResult.data, requester)];
635
+ break;
636
+ case LoadTypes.Playlist: {
637
+ const playlistData = recommendedResult.data;
638
+ tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
639
+ playlist = {
640
+ name: playlistData.info.name,
641
+ playlistInfo: playlistData.pluginInfo,
642
+ requester: requester,
643
+ tracks,
644
+ duration: tracks.reduce((acc, cur) => acc + (cur.duration || 0), 0),
645
+ };
646
+ break;
647
+ }
648
+ }
649
+ const result = { loadType: recommendedResult.loadType, tracks, playlist };
650
+ if (result.loadType === LoadTypes.Empty || result.loadType === LoadTypes.Error) {
651
+ return [];
652
+ }
653
+ if (result.loadType === LoadTypes.Playlist) {
654
+ result.tracks = result.playlist.tracks;
655
+ }
656
+ if (!result.tracks.length) {
657
+ return [];
658
+ }
659
+ return result.tracks;
660
+ }
661
+ break;
606
662
  default:
607
663
  return [];
608
664
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magmastream",
3
- "version": "2.9.0-dev.13",
3
+ "version": "2.9.0-dev.15",
4
4
  "description": "A user-friendly Lavalink client designed for NodeJS.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -90,4 +90,4 @@
90
90
  "homepage": "https://docs.magmastream.com",
91
91
  "author": "Abel Purnwasy",
92
92
  "license": "Apache-2.0"
93
- }
93
+ }