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 +8 -0
- package/dist/structures/Manager.js +362 -161
- package/dist/structures/Node.js +1 -1
- package/dist/structures/Player.js +2 -3
- package/dist/structures/Utils.js +56 -0
- package/package.json +2 -2
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
await
|
|
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
|
-
|
|
454
|
-
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
489
|
-
|
|
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
|
-
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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;
|
package/dist/structures/Node.js
CHANGED
|
@@ -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 =
|
|
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
|
/**
|
package/dist/structures/Utils.js
CHANGED
|
@@ -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.
|
|
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
|
+
}
|