lavalink-client 2.5.0 → 2.5.2
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/cjs/structures/Filters.js +3 -2
- package/dist/cjs/structures/LavalinkManager.d.ts +1 -1
- package/dist/cjs/structures/LavalinkManager.js +15 -15
- package/dist/cjs/structures/LavalinkManagerStatics.js +15 -1
- package/dist/cjs/structures/Node.d.ts +1 -1
- package/dist/cjs/structures/Node.js +20 -10
- package/dist/cjs/structures/Player.js +56 -13
- package/dist/cjs/structures/Types/Manager.d.ts +2 -0
- package/dist/cjs/structures/Types/Track.d.ts +1 -1
- package/dist/cjs/structures/Types/Utils.d.ts +5 -5
- package/dist/cjs/structures/Utils.d.ts +2 -1
- package/dist/cjs/structures/Utils.js +46 -4
- package/dist/esm/structures/Filters.js +3 -2
- package/dist/esm/structures/LavalinkManager.d.ts +1 -1
- package/dist/esm/structures/LavalinkManager.js +16 -16
- package/dist/esm/structures/LavalinkManagerStatics.js +15 -1
- package/dist/esm/structures/Node.d.ts +1 -1
- package/dist/esm/structures/Node.js +21 -11
- package/dist/esm/structures/Player.js +57 -14
- package/dist/esm/structures/Types/Manager.d.ts +2 -0
- package/dist/esm/structures/Types/Track.d.ts +1 -1
- package/dist/esm/structures/Types/Utils.d.ts +5 -5
- package/dist/esm/structures/Utils.d.ts +2 -1
- package/dist/esm/structures/Utils.js +45 -4
- package/dist/types/structures/LavalinkManager.d.ts +1 -1
- package/dist/types/structures/Node.d.ts +1 -1
- package/dist/types/structures/Types/Manager.d.ts +2 -0
- package/dist/types/structures/Types/Track.d.ts +1 -1
- package/dist/types/structures/Types/Utils.d.ts +5 -5
- package/dist/types/structures/Utils.d.ts +2 -1
- package/package.json +11 -11
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { audioOutputsData } from "./Constants.js";
|
|
2
|
+
import { safeStringify } from "./Utils.js";
|
|
2
3
|
/**
|
|
3
4
|
* The FilterManager for each player
|
|
4
5
|
*/
|
|
@@ -670,7 +671,7 @@ export class FilterManager {
|
|
|
670
671
|
async setEQ(bands) {
|
|
671
672
|
if (!Array.isArray(bands))
|
|
672
673
|
bands = [bands];
|
|
673
|
-
if (!bands.length || !bands.every((band) =>
|
|
674
|
+
if (!bands.length || !bands.every((band) => safeStringify(Object.keys(band).sort()) === '["band","gain"]'))
|
|
674
675
|
throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
|
|
675
676
|
for (const { band, gain } of bands)
|
|
676
677
|
this.equalizerBands[band] = { band, gain };
|
|
@@ -690,6 +691,6 @@ export class FilterManager {
|
|
|
690
691
|
}
|
|
691
692
|
/** Clears the equalizer bands. */
|
|
692
693
|
async clearEQ() {
|
|
693
|
-
return this.setEQ(
|
|
694
|
+
return this.setEQ(Array.from({ length: 15 }, () => ({ band: 0, gain: 0 })));
|
|
694
695
|
}
|
|
695
696
|
}
|
|
@@ -2,9 +2,9 @@ import { EventEmitter } from "events";
|
|
|
2
2
|
import { NodeManager } from "./NodeManager.js";
|
|
3
3
|
import { Player } from "./Player.js";
|
|
4
4
|
import { ManagerUtils, MiniMap } from "./Utils.js";
|
|
5
|
+
import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils.js";
|
|
5
6
|
import type { BotClientOptions, LavalinkManagerEvents, ManagerOptions } from "./Types/Manager.js";
|
|
6
7
|
import type { PlayerOptions } from "./Types/Player.js";
|
|
7
|
-
import type { ChannelDeletePacket, VoicePacket, VoiceServer, VoiceState } from "./Types/Utils.js";
|
|
8
8
|
export declare class LavalinkManager extends EventEmitter {
|
|
9
9
|
/**
|
|
10
10
|
* Emit an event
|
|
@@ -3,7 +3,7 @@ import { DebugEvents, DestroyReasons } from "./Constants.js";
|
|
|
3
3
|
import { NodeManager } from "./NodeManager.js";
|
|
4
4
|
import { Player } from "./Player.js";
|
|
5
5
|
import { DefaultQueueStore } from "./Queue.js";
|
|
6
|
-
import { ManagerUtils, MiniMap } from "./Utils.js";
|
|
6
|
+
import { ManagerUtils, MiniMap, safeStringify } from "./Utils.js";
|
|
7
7
|
export class LavalinkManager extends EventEmitter {
|
|
8
8
|
/**
|
|
9
9
|
* Emit an event
|
|
@@ -68,7 +68,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
68
68
|
applyOptions(options) {
|
|
69
69
|
this.options = {
|
|
70
70
|
client: {
|
|
71
|
-
...
|
|
71
|
+
...options?.client,
|
|
72
72
|
id: options?.client?.id,
|
|
73
73
|
username: options?.client?.username ?? "lavalink-client"
|
|
74
74
|
},
|
|
@@ -311,7 +311,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
311
311
|
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
312
312
|
if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
313
313
|
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
314
|
-
throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${
|
|
314
|
+
throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${safeStringify(oldPlayer.toJSON?.())}`);
|
|
315
315
|
else if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
316
316
|
this.emit("debug", DebugEvents.PlayerDeleteInsteadOfDestroy, {
|
|
317
317
|
state: "warn",
|
|
@@ -353,13 +353,13 @@ export class LavalinkManager extends EventEmitter {
|
|
|
353
353
|
if (this.initiated)
|
|
354
354
|
return this;
|
|
355
355
|
clientData = clientData ?? {};
|
|
356
|
-
this.options.client = { ...
|
|
356
|
+
this.options.client = { ...this.options?.client, ...clientData };
|
|
357
357
|
if (!this.options?.client.id)
|
|
358
358
|
throw new Error('"client.id" is not set. Pass it in Manager#init() or as a option in the constructor.');
|
|
359
359
|
if (typeof this.options?.client.id !== "string")
|
|
360
360
|
throw new Error('"client.id" set is not type of "string"');
|
|
361
361
|
let success = 0;
|
|
362
|
-
for (const node of
|
|
362
|
+
for (const node of this.nodeManager.nodes.values()) {
|
|
363
363
|
try {
|
|
364
364
|
await node.connect();
|
|
365
365
|
success++;
|
|
@@ -436,7 +436,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
436
436
|
if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
437
437
|
this.emit("debug", DebugEvents.NoAudioDebug, {
|
|
438
438
|
state: "warn",
|
|
439
|
-
message: `No Update data found in payload :: ${
|
|
439
|
+
message: `No Update data found in payload :: ${safeStringify(data, 2)}`,
|
|
440
440
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
441
441
|
});
|
|
442
442
|
}
|
|
@@ -448,7 +448,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
448
448
|
if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
449
449
|
this.emit("debug", DebugEvents.NoAudioDebug, {
|
|
450
450
|
state: "error",
|
|
451
|
-
message: `No 'token' nor 'session_id' found in payload :: ${
|
|
451
|
+
message: `No 'token' nor 'session_id' found in payload :: ${safeStringify(data, 2)}`,
|
|
452
452
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
453
453
|
});
|
|
454
454
|
}
|
|
@@ -461,7 +461,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
461
461
|
if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
462
462
|
this.emit("debug", DebugEvents.NoAudioDebug, {
|
|
463
463
|
state: "warn",
|
|
464
|
-
message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${
|
|
464
|
+
message: `No Lavalink Player found via key: 'guild_id' of update-data :: ${safeStringify(update, 2)}`,
|
|
465
465
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
466
466
|
});
|
|
467
467
|
}
|
|
@@ -488,11 +488,11 @@ export class LavalinkManager extends EventEmitter {
|
|
|
488
488
|
if (!sessionId2Use) {
|
|
489
489
|
this.emit("debug", DebugEvents.NoAudioDebug, {
|
|
490
490
|
state: "error",
|
|
491
|
-
message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${
|
|
491
|
+
message: `Can't send updatePlayer for voice token session - Missing sessionId :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, 2)}`,
|
|
492
492
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
493
493
|
});
|
|
494
494
|
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
495
|
-
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function,
|
|
495
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Can't send updatePlayer for voice token session - Missing sessionId", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice });
|
|
496
496
|
}
|
|
497
497
|
else {
|
|
498
498
|
await player.node.updatePlayer({
|
|
@@ -502,18 +502,18 @@ export class LavalinkManager extends EventEmitter {
|
|
|
502
502
|
token: update.token,
|
|
503
503
|
endpoint: update.endpoint,
|
|
504
504
|
sessionId: sessionId2Use,
|
|
505
|
-
}
|
|
506
|
-
}
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
507
|
});
|
|
508
508
|
if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
509
509
|
this.emit("debug", DebugEvents.NoAudioDebug, {
|
|
510
510
|
state: "log",
|
|
511
|
-
message: `Sent updatePlayer for voice token session :: ${
|
|
511
|
+
message: `Sent updatePlayer for voice token session :: ${safeStringify({ voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, update, playerVoice: player.voice }, 2)}`,
|
|
512
512
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
513
513
|
});
|
|
514
514
|
}
|
|
515
515
|
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
516
|
-
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function,
|
|
516
|
+
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session", { voice: { token: update.token, endpoint: update.endpoint, sessionId: sessionId2Use, }, playerVoice: player.voice, update });
|
|
517
517
|
}
|
|
518
518
|
return;
|
|
519
519
|
}
|
|
@@ -541,12 +541,12 @@ export class LavalinkManager extends EventEmitter {
|
|
|
541
541
|
if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
542
542
|
this.emit("debug", DebugEvents.NoAudioDebug, {
|
|
543
543
|
state: "warn",
|
|
544
|
-
message: `Function to assing sessionId provided, but no found in Payload: ${
|
|
544
|
+
message: `Function to assing sessionId provided, but no found in Payload: ${safeStringify({ update, playerVoice: player.voice }, 2)}`,
|
|
545
545
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
546
546
|
});
|
|
547
547
|
}
|
|
548
548
|
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
549
|
-
console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${
|
|
549
|
+
console.debug(`Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Function to assing sessionId provided, but no found in Payload: ${safeStringify(update, 2)}`);
|
|
550
550
|
}
|
|
551
551
|
player.voiceChannelId = update.channel_id;
|
|
552
552
|
const selfMuteChanged = typeof update.self_mute === "boolean" && player.voiceState.selfMute !== update.self_mute;
|
|
@@ -48,6 +48,12 @@ export const DefaultSources = {
|
|
|
48
48
|
"vkmusic": "vksearch",
|
|
49
49
|
"vk music": "vksearch",
|
|
50
50
|
"vkrec": "vkrec",
|
|
51
|
+
"vk": "vksearch",
|
|
52
|
+
// Qobuz (lavasrc)
|
|
53
|
+
"qbsearch": "qbsearch",
|
|
54
|
+
"qobuz": "qbsearch",
|
|
55
|
+
"qbisrc": "qbisrc",
|
|
56
|
+
"qbrec": "qbrec",
|
|
51
57
|
// speak PLUGIN
|
|
52
58
|
"speak": "speak",
|
|
53
59
|
"tts": "tts",
|
|
@@ -70,6 +76,12 @@ export const DefaultSources = {
|
|
|
70
76
|
"https": "https",
|
|
71
77
|
"link": "link",
|
|
72
78
|
"uri": "uri",
|
|
79
|
+
// tidal
|
|
80
|
+
"tidal": "tdsearch",
|
|
81
|
+
"td": "tdsearch",
|
|
82
|
+
"tidal music": "tdsearch",
|
|
83
|
+
"tdsearch": "tdsearch",
|
|
84
|
+
"tdrec": "tdrec",
|
|
73
85
|
// jiosaavn
|
|
74
86
|
"jiosaavn": "jssearch",
|
|
75
87
|
"js": "jssearch",
|
|
@@ -122,8 +134,10 @@ export const SourceLinksRegexes = {
|
|
|
122
134
|
SpotifyAlbumRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?album\/(?<identifier>[a-zA-Z0-9-_]+)/,
|
|
123
135
|
AllSpotifyRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?(?<type>track|album|playlist|artist|episode|show)\/(?<identifier>[a-zA-Z0-9-_]+)/,
|
|
124
136
|
appleMusic: /https?:\/\/?(?:www\.)?music\.apple\.com\/(\S+)/,
|
|
137
|
+
/** From tidal */
|
|
138
|
+
tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
|
|
125
139
|
/** From jiosaavn-plugin */
|
|
126
|
-
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_
|
|
140
|
+
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
|
|
127
141
|
/** FROM DUNCTE BOT PLUGIN */
|
|
128
142
|
tiktok: /https:\/\/www\.tiktok\.com\//,
|
|
129
143
|
mixcloud: /https:\/\/www\.mixcloud\.com\//,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils.js";
|
|
1
2
|
import type { Player } from "./Player.js";
|
|
2
3
|
import type { DestroyReasonsType, DisconnectReasonsType } from "./Types/Player.js";
|
|
3
4
|
import type { Track } from "./Types/Track.js";
|
|
4
|
-
import type { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Types/Utils.js";
|
|
5
5
|
import type { NodeManager } from "./NodeManager.js";
|
|
6
6
|
import type { BaseNodeStats, LavalinkInfo, LavalinkNodeOptions, LyricsResult, ModifyRequest, NodeStats, SponsorBlockSegment } from "./Types/Node.js";
|
|
7
7
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isAbsolute } from "path";
|
|
2
2
|
import WebSocket from "ws";
|
|
3
3
|
import { DebugEvents, DestroyReasons, validSponsorBlocks } from "./Constants.js";
|
|
4
|
-
import { NodeSymbol, queueTrackEnd } from "./Utils.js";
|
|
4
|
+
import { NodeSymbol, queueTrackEnd, safeStringify } from "./Utils.js";
|
|
5
5
|
/**
|
|
6
6
|
* Lavalink Node creator class
|
|
7
7
|
*/
|
|
@@ -130,7 +130,7 @@ export class LavalinkNode {
|
|
|
130
130
|
if (["DELETE", "PUT"].includes(options.method))
|
|
131
131
|
return;
|
|
132
132
|
if (response.status === 404)
|
|
133
|
-
throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${
|
|
133
|
+
throw new Error(`Node Request resulted into an error, request-PATH: ${options.path} | headers: ${safeStringify(response.headers)}`);
|
|
134
134
|
return parseAsText ? await response.text() : await response.json();
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
@@ -262,7 +262,7 @@ export class LavalinkNode {
|
|
|
262
262
|
const res = await this.request(`/sessions/${this.sessionId}/players/${data.guildId}`, r => {
|
|
263
263
|
r.method = "PATCH";
|
|
264
264
|
r.headers["Content-Type"] = "application/json";
|
|
265
|
-
r.body =
|
|
265
|
+
r.body = safeStringify(data.playerOptions);
|
|
266
266
|
if (data.noReplace) {
|
|
267
267
|
const url = new URL(`${this.restAddress}${r.path}`);
|
|
268
268
|
url.searchParams.append("noReplace", data.noReplace === true && typeof data.noReplace === "boolean" ? "true" : "false");
|
|
@@ -272,7 +272,7 @@ export class LavalinkNode {
|
|
|
272
272
|
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
273
273
|
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateSuccess, {
|
|
274
274
|
state: "log",
|
|
275
|
-
message: `Player get's updated with following payload :: ${
|
|
275
|
+
message: `Player get's updated with following payload :: ${safeStringify(data.playerOptions, 3)}`,
|
|
276
276
|
functionLayer: "LavalinkNode > node > updatePlayer()",
|
|
277
277
|
});
|
|
278
278
|
}
|
|
@@ -584,7 +584,7 @@ export class LavalinkNode {
|
|
|
584
584
|
return this.request(`/sessions/${this.sessionId}`, r => {
|
|
585
585
|
r.method = "PATCH";
|
|
586
586
|
r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
|
|
587
|
-
r.body =
|
|
587
|
+
r.body = safeStringify(data);
|
|
588
588
|
});
|
|
589
589
|
}
|
|
590
590
|
/**
|
|
@@ -628,7 +628,7 @@ export class LavalinkNode {
|
|
|
628
628
|
// return the decoded + builded tracks
|
|
629
629
|
return await this.request(`/decodetracks`, r => {
|
|
630
630
|
r.method = "POST";
|
|
631
|
-
r.body =
|
|
631
|
+
r.body = safeStringify(encodeds);
|
|
632
632
|
r.headers["Content-Type"] = "application/json";
|
|
633
633
|
}).then((r) => r.map(track => this.NodeManager.LavalinkManager.utils.buildTrack(track, requester)));
|
|
634
634
|
}
|
|
@@ -804,7 +804,7 @@ export class LavalinkNode {
|
|
|
804
804
|
return await this.request(`/routeplanner/free/address`, r => {
|
|
805
805
|
r.method = "POST";
|
|
806
806
|
r.headers["Content-Type"] = "application/json";
|
|
807
|
-
r.body =
|
|
807
|
+
r.body = safeStringify({ address });
|
|
808
808
|
});
|
|
809
809
|
},
|
|
810
810
|
/**
|
|
@@ -1214,6 +1214,9 @@ export class LavalinkNode {
|
|
|
1214
1214
|
return this.queueEnd(player, track, payload);
|
|
1215
1215
|
// If a track had an error while starting
|
|
1216
1216
|
if (["loadFailed", "cleanup"].includes(payload.reason)) {
|
|
1217
|
+
//Dont add tracks if the player is already destroying.
|
|
1218
|
+
if (player.get("internal_destroystatus") === true)
|
|
1219
|
+
return;
|
|
1217
1220
|
await queueTrackEnd(player);
|
|
1218
1221
|
// if no track available, end queue
|
|
1219
1222
|
if (!player.queue.current)
|
|
@@ -1267,8 +1270,15 @@ export class LavalinkNode {
|
|
|
1267
1270
|
}
|
|
1268
1271
|
this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
|
|
1269
1272
|
// If there are no songs in the queue
|
|
1270
|
-
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
1271
|
-
|
|
1273
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
|
|
1274
|
+
try { //Sometimes the trackStuck event triggers from the Lavalink server, but the track continues playing or resumes after. We explicitly end the track in such cases
|
|
1275
|
+
await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
catch {
|
|
1279
|
+
return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1272
1282
|
// remove the current track, and enqueue the next one
|
|
1273
1283
|
await queueTrackEnd(player);
|
|
1274
1284
|
// if no track available, end queue
|
|
@@ -1277,7 +1287,7 @@ export class LavalinkNode {
|
|
|
1277
1287
|
}
|
|
1278
1288
|
// play track if autoSkip is true
|
|
1279
1289
|
if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
|
|
1280
|
-
player.play({ noReplace:
|
|
1290
|
+
player.play({ track: player.queue.current, noReplace: false }); // Replace the stuck track with the new track.
|
|
1281
1291
|
}
|
|
1282
1292
|
return;
|
|
1283
1293
|
}
|
|
@@ -1370,7 +1380,7 @@ export class LavalinkNode {
|
|
|
1370
1380
|
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
1371
1381
|
r.method = "PUT";
|
|
1372
1382
|
r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
|
|
1373
|
-
r.body =
|
|
1383
|
+
r.body = safeStringify(segments.map(v => v.toLowerCase()));
|
|
1374
1384
|
});
|
|
1375
1385
|
if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
1376
1386
|
this.NodeManager.LavalinkManager.emit("debug", DebugEvents.SetSponsorBlock, {
|
|
@@ -2,7 +2,7 @@ import { DebugEvents } from "./Constants.js";
|
|
|
2
2
|
import { bandCampSearch } from "./CustomSearches/BandCampSearch.js";
|
|
3
3
|
import { FilterManager } from "./Filters.js";
|
|
4
4
|
import { Queue, QueueSaver } from "./Queue.js";
|
|
5
|
-
import { queueTrackEnd } from "./Utils.js";
|
|
5
|
+
import { queueTrackEnd, safeStringify } from "./Utils.js";
|
|
6
6
|
export class Player {
|
|
7
7
|
/** Filter Manager per player */
|
|
8
8
|
filterManager;
|
|
@@ -184,7 +184,11 @@ export class Player {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
if ((typeof options.track?.userData === "object" || typeof options.clientTrack?.userData === "object") && options.clientTrack)
|
|
187
|
-
options.clientTrack.userData = {
|
|
187
|
+
options.clientTrack.userData = {
|
|
188
|
+
...(typeof options?.clientTrack?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.clientTrack?.requester || {}) } : {}),
|
|
189
|
+
...options?.clientTrack.userData,
|
|
190
|
+
...options.track?.userData,
|
|
191
|
+
};
|
|
188
192
|
options.track = {
|
|
189
193
|
encoded: options.clientTrack?.encoded,
|
|
190
194
|
requester: options.clientTrack?.requester,
|
|
@@ -206,16 +210,11 @@ export class Player {
|
|
|
206
210
|
const track = Object.fromEntries(Object.entries({
|
|
207
211
|
encoded: options.track.encoded,
|
|
208
212
|
identifier: options.track.identifier,
|
|
213
|
+
userData: {
|
|
214
|
+
...(typeof options?.track?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {}) } : {}),
|
|
215
|
+
...options.track.userData,
|
|
216
|
+
}
|
|
209
217
|
}).filter(v => typeof v[1] !== "undefined"));
|
|
210
|
-
if (typeof options.track.userData === "object")
|
|
211
|
-
track.userData = {
|
|
212
|
-
...(options.track.userData || {})
|
|
213
|
-
};
|
|
214
|
-
if (typeof options?.track?.requester === "object")
|
|
215
|
-
track.userData = {
|
|
216
|
-
...(track.userData || {}),
|
|
217
|
-
requester: this.LavalinkManager.utils.getTransformedRequester(options?.track?.requester || {})
|
|
218
|
-
};
|
|
219
218
|
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
220
219
|
this.LavalinkManager.emit("debug", DebugEvents.PlayerPlayWithTrackReplace, {
|
|
221
220
|
state: "log",
|
|
@@ -251,7 +250,11 @@ export class Player {
|
|
|
251
250
|
// resolve the unresolved track
|
|
252
251
|
await this.queue.current.resolve(this);
|
|
253
252
|
if (typeof options.track?.userData === "object" && this.queue.current)
|
|
254
|
-
this.queue.current.userData = {
|
|
253
|
+
this.queue.current.userData = {
|
|
254
|
+
...(typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {}),
|
|
255
|
+
...this.queue.current?.userData,
|
|
256
|
+
...options.track?.userData
|
|
257
|
+
};
|
|
255
258
|
}
|
|
256
259
|
catch (error) {
|
|
257
260
|
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
@@ -267,6 +270,8 @@ export class Player {
|
|
|
267
270
|
delete options.clientTrack;
|
|
268
271
|
if (options && "track" in options)
|
|
269
272
|
delete options.track;
|
|
273
|
+
// get rid of the current song without shifting the queue, so that the shifting can happen inside the next .play() call when "autoSkipOnResolveError" is true
|
|
274
|
+
await queueTrackEnd(this, true);
|
|
270
275
|
// try to play the next track if possible
|
|
271
276
|
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
272
277
|
return this.play(options);
|
|
@@ -287,7 +292,11 @@ export class Player {
|
|
|
287
292
|
track: {
|
|
288
293
|
encoded: this.queue.current?.encoded || null,
|
|
289
294
|
// identifier: options.identifier,
|
|
290
|
-
userData:
|
|
295
|
+
userData: {
|
|
296
|
+
...(typeof this.queue.current?.requester === "object" ? { requester: this.LavalinkManager.utils.getTransformedRequester(this.queue.current?.requester || {}) } : {}),
|
|
297
|
+
...options?.track?.userData,
|
|
298
|
+
...this.queue.current?.userData,
|
|
299
|
+
},
|
|
291
300
|
},
|
|
292
301
|
volume: this.lavalinkVolume,
|
|
293
302
|
position: options?.position ?? 0,
|
|
@@ -402,6 +411,8 @@ export class Player {
|
|
|
402
411
|
const now = performance.now();
|
|
403
412
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
|
|
404
413
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
414
|
+
// emit the event
|
|
415
|
+
this.LavalinkManager.emit("playerPaused", this, this.queue.current);
|
|
405
416
|
return this;
|
|
406
417
|
}
|
|
407
418
|
/**
|
|
@@ -414,6 +425,8 @@ export class Player {
|
|
|
414
425
|
const now = performance.now();
|
|
415
426
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
|
|
416
427
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
428
|
+
// emit the event
|
|
429
|
+
this.LavalinkManager.emit("playerResumed", this, this.queue.current);
|
|
417
430
|
return this;
|
|
418
431
|
}
|
|
419
432
|
/**
|
|
@@ -679,6 +692,7 @@ export class Player {
|
|
|
679
692
|
}
|
|
680
693
|
const data = this.toJSON();
|
|
681
694
|
const currentTrack = this.queue.current;
|
|
695
|
+
const segments = await this.getSponsorBlock().catch(() => []);
|
|
682
696
|
const voiceData = this.voice;
|
|
683
697
|
if (!voiceData.endpoint ||
|
|
684
698
|
!voiceData.sessionId ||
|
|
@@ -695,7 +709,7 @@ export class Player {
|
|
|
695
709
|
await this.node.request(endpoint, r => {
|
|
696
710
|
r.method = "PATCH";
|
|
697
711
|
r.headers["Content-Type"] = "application/json";
|
|
698
|
-
r.body =
|
|
712
|
+
r.body = safeStringify({
|
|
699
713
|
voice: {
|
|
700
714
|
token: voiceData.token,
|
|
701
715
|
endpoint: voiceData.endpoint,
|
|
@@ -703,6 +717,33 @@ export class Player {
|
|
|
703
717
|
}
|
|
704
718
|
});
|
|
705
719
|
});
|
|
720
|
+
const hasSponsorBlock = this.node.info?.plugins?.find(v => v.name === "sponsorblock-plugin");
|
|
721
|
+
if (hasSponsorBlock) {
|
|
722
|
+
if (segments.length) {
|
|
723
|
+
await this.setSponsorBlock(segments).catch(error => {
|
|
724
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
725
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerChangeNode, {
|
|
726
|
+
state: "error",
|
|
727
|
+
error: error,
|
|
728
|
+
message: `Player > changeNode() Unable to set SponsorBlock Segments`,
|
|
729
|
+
functionLayer: "Player > changeNode()",
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
await this.setSponsorBlock().catch(error => {
|
|
736
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
737
|
+
this.LavalinkManager.emit("debug", DebugEvents.PlayerChangeNode, {
|
|
738
|
+
state: "error",
|
|
739
|
+
error: error,
|
|
740
|
+
message: `Player > changeNode() Unable to set SponsorBlock Segments`,
|
|
741
|
+
functionLayer: "Player > changeNode()",
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
706
747
|
if (currentTrack) { // If there is a current track, send it to the new node.
|
|
707
748
|
await this.node.updatePlayer({
|
|
708
749
|
guildId: this.guildId,
|
|
@@ -716,6 +757,8 @@ export class Player {
|
|
|
716
757
|
}
|
|
717
758
|
});
|
|
718
759
|
}
|
|
760
|
+
this.paused = data.paused;
|
|
761
|
+
this.playing = data.playing;
|
|
719
762
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
720
763
|
return this.node.id;
|
|
721
764
|
}
|
|
@@ -172,6 +172,8 @@ export interface LavalinkManagerEvents {
|
|
|
172
172
|
* @event Manager#LyricsNotFound
|
|
173
173
|
*/
|
|
174
174
|
"LyricsNotFound": (player: Player, track: Track | UnresolvedTrack | null, payload: LyricsNotFoundEvent) => void;
|
|
175
|
+
"playerResumed": (player: Player, track: Track | UnresolvedTrack | null) => void;
|
|
176
|
+
"playerPaused": (player: Player, track: Track | UnresolvedTrack | null) => void;
|
|
175
177
|
}
|
|
176
178
|
/**
|
|
177
179
|
* The Bot client Options needed for the manager
|
|
@@ -4,7 +4,7 @@ import type { Base64 } from "./Utils.js";
|
|
|
4
4
|
/** Sourcenames provided by lavalink server */
|
|
5
5
|
export type LavalinkSourceNames = "youtube" | "youtubemusic" | "soundcloud" | "bandcamp" | "twitch";
|
|
6
6
|
/** Source Names provided by lava src plugin */
|
|
7
|
-
export type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts";
|
|
7
|
+
export type LavalinkPlugin_LavaSrc_SourceNames = "deezer" | "spotify" | "applemusic" | "yandexmusic" | "flowery-tts" | "vkmusic" | "tidal" | "qobuz";
|
|
8
8
|
/** Source Names provided by jiosaavan plugin */
|
|
9
9
|
export type LavalinkPlugin_JioSaavn_SourceNames = "jiosaavn";
|
|
10
10
|
/** The SourceNames provided by lavalink */
|
|
@@ -11,7 +11,7 @@ export type Opaque<T, K> = T & {
|
|
|
11
11
|
export type IntegerNumber = Opaque<number, 'Int'>;
|
|
12
12
|
/** Opqaue tyep for floatnumber */
|
|
13
13
|
export type FloatNumber = Opaque<number, 'Float'>;
|
|
14
|
-
export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "dzrec" | "ymsearch" | "ymrec" | "vksearch" | "vkrec";
|
|
14
|
+
export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "dzrec" | "ymsearch" | "ymrec" | "vksearch" | "vkrec" | "tdsearch" | "tdrec" | "qbsearch" | "qbisrc" | "qbrec";
|
|
15
15
|
export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
|
|
16
16
|
export type JioSaavnSearchPlatform = "jssearch" | "jsrec";
|
|
17
17
|
export type DuncteSearchPlatform = "speak" | "phsearch" | "pornhub" | "porn" | "tts";
|
|
@@ -20,9 +20,9 @@ export type LavalinkClientSearchPlatformResolve = "bandcamp" | "bc";
|
|
|
20
20
|
export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "bcsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform | JioSaavnSearchPlatform | LavalinkClientSearchPlatform;
|
|
21
21
|
export type ClientCustomSearchPlatformUtils = "local" | "http" | "https" | "link" | "uri";
|
|
22
22
|
export type ClientSearchPlatform = ClientCustomSearchPlatformUtils | // for file/link requests
|
|
23
|
-
"youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "vk music" | "vkmusic" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform | "js" | "jiosaavn";
|
|
23
|
+
"youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "musicyoutube" | "music youtube" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "musicapple" | "music apple" | "sp" | "spsuggestion" | "spotify" | "spotify.com" | "spotifycom" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic" | "vk" | "vk music" | "vkmusic" | "tidal" | "tidal music" | "qobuz" | "flowerytts" | "flowery" | "flowery.tts" | LavalinkClientSearchPlatformResolve | LavalinkClientSearchPlatform | "js" | "jiosaavn" | "td" | "tidal" | "tdrec";
|
|
24
24
|
export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
|
|
25
|
-
export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "jiosaavn" | "appleMusic" | "TwitchTv" | "vimeo";
|
|
25
|
+
export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "jiosaavn" | "appleMusic" | "tidal" | "TwitchTv" | "vimeo";
|
|
26
26
|
export interface PlaylistInfo {
|
|
27
27
|
/** The playlist name */
|
|
28
28
|
name: string;
|
|
@@ -92,13 +92,13 @@ export interface TrackEndEvent extends PlayerEvent {
|
|
|
92
92
|
export interface TrackExceptionEvent extends PlayerEvent {
|
|
93
93
|
type: "TrackExceptionEvent";
|
|
94
94
|
exception?: Exception;
|
|
95
|
-
|
|
95
|
+
track: LavalinkTrack;
|
|
96
96
|
error: string;
|
|
97
97
|
}
|
|
98
98
|
export interface TrackStuckEvent extends PlayerEvent {
|
|
99
99
|
type: "TrackStuckEvent";
|
|
100
100
|
thresholdMs: number;
|
|
101
|
-
|
|
101
|
+
track: LavalinkTrack;
|
|
102
102
|
}
|
|
103
103
|
export interface WebSocketClosedEvent extends PlayerEvent {
|
|
104
104
|
type: "WebSocketClosedEvent";
|
|
@@ -112,4 +112,5 @@ export declare class MiniMap<K, V> extends Map<K, V> {
|
|
|
112
112
|
map<T>(fn: (value: V, key: K, miniMap: this) => T): T[];
|
|
113
113
|
map<This, T>(fn: (this: This, value: V, key: K, miniMap: this) => T, thisArg: This): T[];
|
|
114
114
|
}
|
|
115
|
-
export declare function queueTrackEnd(player: Player): Promise<Track>;
|
|
115
|
+
export declare function queueTrackEnd(player: Player, dontShiftQueue?: boolean): Promise<Track>;
|
|
116
|
+
export declare function safeStringify(obj: any, padding?: number): string;
|
|
@@ -302,6 +302,9 @@ export class ManagerUtils {
|
|
|
302
302
|
if (SourceLinksRegexes.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
303
303
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled");
|
|
304
304
|
}
|
|
305
|
+
if (SourceLinksRegexes.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
|
|
306
|
+
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tidal' enabled");
|
|
307
|
+
}
|
|
305
308
|
return;
|
|
306
309
|
}
|
|
307
310
|
transformQuery(query) {
|
|
@@ -366,6 +369,12 @@ export class ManagerUtils {
|
|
|
366
369
|
if (source === "speak" && !node.info?.plugins?.find(c => c.name.toLowerCase().includes(LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
|
|
367
370
|
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
|
|
368
371
|
}
|
|
372
|
+
if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
373
|
+
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdsearch' work");
|
|
374
|
+
}
|
|
375
|
+
if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
376
|
+
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdrec' work");
|
|
377
|
+
}
|
|
369
378
|
if (source === "tts" && !node.info?.plugins?.find(c => c.name.toLowerCase().includes(LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
|
|
370
379
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
371
380
|
}
|
|
@@ -381,6 +390,21 @@ export class ManagerUtils {
|
|
|
381
390
|
if (source === "ytsearch" && !node.info?.sourceManagers?.includes("youtube")) {
|
|
382
391
|
throw new Error("Lavalink Node has not 'youtube' enabled, which is required to have 'ytsearch' work");
|
|
383
392
|
}
|
|
393
|
+
if (source === "vksearch" && !node.info?.sourceManagers?.includes("vkmusic")) {
|
|
394
|
+
throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vksearch' work");
|
|
395
|
+
}
|
|
396
|
+
if (source === "vkrec" && !node.info?.sourceManagers?.includes("vkmusic")) {
|
|
397
|
+
throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vkrec' work");
|
|
398
|
+
}
|
|
399
|
+
if (source === "qbsearch" && !node.info?.sourceManagers?.includes("qobuz")) {
|
|
400
|
+
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbsearch' work");
|
|
401
|
+
}
|
|
402
|
+
if (source === "qbisrc" && !node.info?.sourceManagers?.includes("qobuz")) {
|
|
403
|
+
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbisrc' work");
|
|
404
|
+
}
|
|
405
|
+
if (source === "qbrec" && !node.info?.sourceManagers?.includes("qobuz")) {
|
|
406
|
+
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbrec' work");
|
|
407
|
+
}
|
|
384
408
|
return;
|
|
385
409
|
}
|
|
386
410
|
}
|
|
@@ -411,7 +435,7 @@ export class MiniMap extends Map {
|
|
|
411
435
|
});
|
|
412
436
|
}
|
|
413
437
|
}
|
|
414
|
-
export async function queueTrackEnd(player) {
|
|
438
|
+
export async function queueTrackEnd(player, dontShiftQueue = false) {
|
|
415
439
|
if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
416
440
|
player.queue.previous.unshift(player.queue.current);
|
|
417
441
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
@@ -422,9 +446,9 @@ export async function queueTrackEnd(player) {
|
|
|
422
446
|
if (player.repeatMode === "queue" && player.queue.current)
|
|
423
447
|
player.queue.tracks.push(player.queue.current);
|
|
424
448
|
// change the current Track to the next upcoming one
|
|
425
|
-
const nextSong = player.queue.tracks.shift();
|
|
449
|
+
const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
|
|
426
450
|
try {
|
|
427
|
-
if (player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
451
|
+
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
428
452
|
await nextSong.resolve(player);
|
|
429
453
|
player.queue.current = nextSong || null;
|
|
430
454
|
// save it in the DB
|
|
@@ -441,7 +465,7 @@ export async function queueTrackEnd(player) {
|
|
|
441
465
|
}
|
|
442
466
|
player.LavalinkManager.emit("trackError", player, player.queue.current, error);
|
|
443
467
|
// try to play the next track if possible
|
|
444
|
-
if (player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
468
|
+
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
445
469
|
return queueTrackEnd(player);
|
|
446
470
|
}
|
|
447
471
|
// return the new current Track
|
|
@@ -516,3 +540,20 @@ async function getClosestTrack(data, player) {
|
|
|
516
540
|
return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
|
|
517
541
|
});
|
|
518
542
|
}
|
|
543
|
+
export function safeStringify(obj, padding = 0) {
|
|
544
|
+
const seen = new WeakSet();
|
|
545
|
+
return JSON.stringify(obj, (key, value) => {
|
|
546
|
+
if (typeof value === "function")
|
|
547
|
+
return undefined; // Funktion skippen
|
|
548
|
+
if (typeof value === "symbol")
|
|
549
|
+
return undefined; // Symbol skippen
|
|
550
|
+
if (typeof value === "bigint")
|
|
551
|
+
return value.toString(); // BigInt to String
|
|
552
|
+
if (typeof value === "object" && value !== null) {
|
|
553
|
+
if (seen.has(value))
|
|
554
|
+
return "[Circular]";
|
|
555
|
+
seen.add(value);
|
|
556
|
+
}
|
|
557
|
+
return value;
|
|
558
|
+
}, padding);
|
|
559
|
+
}
|