lavalink-client 2.4.7 → 2.5.1

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.
Files changed (32) hide show
  1. package/README.md +24 -0
  2. package/dist/cjs/structures/CustomSearches/BandCampSearch.js +3 -1
  3. package/dist/cjs/structures/LavalinkManager.js +4 -4
  4. package/dist/cjs/structures/LavalinkManagerStatics.js +21 -0
  5. package/dist/cjs/structures/Node.d.ts +0 -12
  6. package/dist/cjs/structures/Node.js +32 -48
  7. package/dist/cjs/structures/Player.js +39 -0
  8. package/dist/cjs/structures/Types/Manager.d.ts +2 -0
  9. package/dist/cjs/structures/Types/Player.d.ts +2 -0
  10. package/dist/cjs/structures/Types/Track.d.ts +1 -1
  11. package/dist/cjs/structures/Types/Utils.d.ts +5 -5
  12. package/dist/cjs/structures/Utils.d.ts +2 -2
  13. package/dist/cjs/structures/Utils.js +30 -6
  14. package/dist/esm/structures/CustomSearches/BandCampSearch.js +3 -1
  15. package/dist/esm/structures/LavalinkManager.js +4 -4
  16. package/dist/esm/structures/LavalinkManagerStatics.js +21 -0
  17. package/dist/esm/structures/Node.d.ts +0 -12
  18. package/dist/esm/structures/Node.js +32 -48
  19. package/dist/esm/structures/Player.js +39 -0
  20. package/dist/esm/structures/Types/Manager.d.ts +2 -0
  21. package/dist/esm/structures/Types/Player.d.ts +2 -0
  22. package/dist/esm/structures/Types/Track.d.ts +1 -1
  23. package/dist/esm/structures/Types/Utils.d.ts +5 -5
  24. package/dist/esm/structures/Utils.d.ts +2 -2
  25. package/dist/esm/structures/Utils.js +30 -6
  26. package/dist/types/structures/Node.d.ts +0 -12
  27. package/dist/types/structures/Types/Manager.d.ts +2 -0
  28. package/dist/types/structures/Types/Player.d.ts +2 -0
  29. package/dist/types/structures/Types/Track.d.ts +1 -1
  30. package/dist/types/structures/Types/Utils.d.ts +5 -5
  31. package/dist/types/structures/Utils.d.ts +2 -2
  32. package/package.json +11 -11
@@ -36,11 +36,24 @@ export const DefaultSources = {
36
36
  "dz": "dzsearch",
37
37
  "dzsearch": "dzsearch",
38
38
  "dzisrc": "dzisrc",
39
+ "dzrec": "dzrec",
39
40
  // yandexmusic
40
41
  "yandex music": "ymsearch",
41
42
  "yandexmusic": "ymsearch",
42
43
  "yandex": "ymsearch",
43
44
  "ymsearch": "ymsearch",
45
+ "ymrec": "ymrec",
46
+ // VK Music (lavasrc)
47
+ "vksearch": "vksearch",
48
+ "vkmusic": "vksearch",
49
+ "vk music": "vksearch",
50
+ "vkrec": "vkrec",
51
+ "vk": "vksearch",
52
+ // Qobuz (lavasrc)
53
+ "qbsearch": "qbsearch",
54
+ "qobuz": "qbsearch",
55
+ "qbisrc": "qbisrc",
56
+ "qbrec": "qbrec",
44
57
  // speak PLUGIN
45
58
  "speak": "speak",
46
59
  "tts": "tts",
@@ -63,6 +76,12 @@ export const DefaultSources = {
63
76
  "https": "https",
64
77
  "link": "link",
65
78
  "uri": "uri",
79
+ // tidal
80
+ "tidal": "tdsearch",
81
+ "td": "tdsearch",
82
+ "tidal music": "tdsearch",
83
+ "tdsearch": "tdsearch",
84
+ "tdrec": "tdrec",
66
85
  // jiosaavn
67
86
  "jiosaavn": "jssearch",
68
87
  "js": "jssearch",
@@ -115,6 +134,8 @@ export const SourceLinksRegexes = {
115
134
  SpotifyAlbumRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?album\/(?<identifier>[a-zA-Z0-9-_]+)/,
116
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-_]+)/,
117
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-_]+)/,
118
139
  /** From jiosaavn-plugin */
119
140
  jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_\/,]+)/,
120
141
  /** FROM DUNCTE BOT PLUGIN */
@@ -53,18 +53,6 @@ export declare class LavalinkNode {
53
53
  * ```
54
54
  */
55
55
  constructor(options: LavalinkNodeOptions, manager: NodeManager);
56
- /**
57
- * Parse url params correctly for lavalink requests, including support for urls and uris.
58
- * @param url input url object
59
- * @param extraQueryUrlParams UrlSearchParams to use in a encodedURI, useful for example for flowertts
60
- * @returns the url as a valid string
61
- *
62
- * @example
63
- * ```ts
64
- * player.node.getRequestingUrl(new URL(`http://localhost:2333/v4/loadtracks?identifier=Never gonna give you up`));
65
- * ```
66
- */
67
- private getRequestingUrl;
68
56
  /**
69
57
  * Raw Request util function
70
58
  * @param endpoint endpoint string
@@ -87,32 +87,6 @@ export class LavalinkNode {
87
87
  this.options.regions = (this.options.regions || []).map(a => a.toLowerCase());
88
88
  Object.defineProperty(this, NodeSymbol, { configurable: true, value: true });
89
89
  }
90
- /**
91
- * Parse url params correctly for lavalink requests, including support for urls and uris.
92
- * @param url input url object
93
- * @param extraQueryUrlParams UrlSearchParams to use in a encodedURI, useful for example for flowertts
94
- * @returns the url as a valid string
95
- *
96
- * @example
97
- * ```ts
98
- * player.node.getRequestingUrl(new URL(`http://localhost:2333/v4/loadtracks?identifier=Never gonna give you up`));
99
- * ```
100
- */
101
- getRequestingUrl(url, extraQueryUrlParams) {
102
- if (!url.searchParams.size)
103
- return `${url.origin}${url.pathname}`;
104
- const keysToAdd = [];
105
- for (const [paramKey, paramValue] of url.searchParams.entries()) {
106
- const decoded = decodeURIComponent(paramValue).trim(); // double decoding, once internally, a second time if decoded by provided user.
107
- if (decoded.includes("://") && !/^https?:\/\//.test(decoded)) { // uri, but not url.
108
- const [key, ...values] = decoded.split("://");
109
- keysToAdd.push(`${paramKey}=${encodeURI(`${key}://${encodeURIComponent(values.join("://"))}${extraQueryUrlParams && extraQueryUrlParams?.size > 0 ? `?${extraQueryUrlParams.toString()}` : ""}`)}`);
110
- continue;
111
- }
112
- keysToAdd.push(`${paramKey}=${encodeURIComponent(decoded)}`);
113
- }
114
- return `${url.origin}${url.pathname}?${keysToAdd.join("&")}`;
115
- }
116
90
  /**
117
91
  * Raw Request util function
118
92
  * @param endpoint endpoint string
@@ -127,7 +101,7 @@ export class LavalinkNode {
127
101
  */
128
102
  async rawRequest(endpoint, modify) {
129
103
  const options = {
130
- path: `/${this.version}/${endpoint.replace(/^\//gm, "")}`,
104
+ path: `/${this.version}/${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`,
131
105
  method: "GET",
132
106
  headers: {
133
107
  "Authorization": this.options.authorization
@@ -137,7 +111,12 @@ export class LavalinkNode {
137
111
  modify?.(options);
138
112
  const url = new URL(`${this.restAddress}${options.path}`);
139
113
  url.searchParams.append("trace", "true");
140
- const urlToUse = this.getRequestingUrl(url, options?.extraQueryUrlParams);
114
+ if (options.extraQueryUrlParams && options.extraQueryUrlParams?.size > 0) {
115
+ for (const [paramKey, paramValue] of options.extraQueryUrlParams.entries()) {
116
+ url.searchParams.append(paramKey, paramValue);
117
+ }
118
+ }
119
+ const urlToUse = url.toString();
141
120
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
142
121
  const { path, extraQueryUrlParams, ...fetchOptions } = options; // destructure fetch only options
143
122
  const response = await fetch(urlToUse, fetchOptions);
@@ -176,20 +155,17 @@ export class LavalinkNode {
176
155
  if (["bcsearch", "bandcamp"].includes(Query.source) && !this.info.sourceManagers.includes("bandcamp")) {
177
156
  throw new Error("Bandcamp Search only works on the player (lavaplayer version < 2.2.0!");
178
157
  }
179
- let uri = `/loadtracks?identifier=`;
158
+ const requestUrl = new URL(`${this.restAddress}/loadtracks`);
180
159
  if (/^https?:\/\//.test(Query.query) || ["http", "https", "link", "uri"].includes(Query.source)) { // if it's a link simply encode it
181
- const url = encodeURIComponent(Query.query);
182
- uri += url;
160
+ requestUrl.searchParams.append("identifier", Query.query);
183
161
  }
184
162
  else { // if not make a query out of it
185
- if (Query.source !== "local")
186
- uri += `${Query.source}:`; // only add the query source string if it's not a local track
187
- if (Query.source === "ftts")
188
- uri += `//${encodeURIComponent(Query.query)}`;
189
- else
190
- uri += encodeURIComponent(Query.query);
163
+ const fttsPrefix = Query.source === "ftts" ? "//" : "";
164
+ const prefix = Query.source !== "local" ? `${Query.source}:${fttsPrefix}` : "";
165
+ requestUrl.searchParams.append("identifier", `${prefix}${Query.query}`);
191
166
  }
192
- const res = await this.request(uri, (options) => {
167
+ const requestPathAndSearch = requestUrl.pathname + requestUrl.search;
168
+ const res = await this.request(requestPathAndSearch, (options) => {
193
169
  if (typeof query === "object" && typeof query.extraQueryUrlParams?.size === "number" && query.extraQueryUrlParams?.size > 0) {
194
170
  options.extraQueryUrlParams = query.extraQueryUrlParams;
195
171
  }
@@ -217,7 +193,7 @@ export class LavalinkNode {
217
193
  thumbnail: (res.data.info?.artworkUrl) || (res.data.pluginInfo?.artworkUrl) || ((typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1) ? null : resTracks[res.data?.info?.selectedTrack] ? (resTracks[res.data?.info?.selectedTrack]?.info?.artworkUrl || resTracks[res.data?.info?.selectedTrack]?.info?.pluginInfo?.artworkUrl) : null) || null,
218
194
  uri: res.data.info?.url || res.data.info?.uri || res.data.info?.link || res.data.pluginInfo?.url || res.data.pluginInfo?.uri || res.data.pluginInfo?.link || null,
219
195
  selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this.NodeManager.LavalinkManager.utils.buildTrack(resTracks[res.data?.info?.selectedTrack], requestUser) : null,
220
- duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.length || 0), 0) : 0,
196
+ duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || cur?.info?.length || 0), 0) : 0,
221
197
  } : null,
222
198
  tracks: (resTracks.length ? resTracks.map(t => this.NodeManager.LavalinkManager.utils.buildTrack(t, requestUser)) : [])
223
199
  };
@@ -1086,7 +1062,7 @@ export class LavalinkNode {
1086
1062
  player.ping.ws = payload.state.ping >= 0 ? payload.state.ping : player.ping.ws <= 0 && player.connected ? null : player.ping.ws || 0;
1087
1063
  if (!player.createdTimeStamp && payload.state.time)
1088
1064
  player.createdTimeStamp = payload.state.time;
1089
- if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600_000) || isAbsolute(player.queue.current?.info?.uri))) {
1065
+ if (player.filterManager.filterUpdatedState === true && ((player.queue.current?.info?.duration || 0) <= (player.LavalinkManager.options.advancedOptions.maxFilterFixDuration || 600_000) || (player.queue.current?.info?.uri && isAbsolute(player.queue.current?.info?.uri)))) {
1090
1066
  player.filterManager.filterUpdatedState = false;
1091
1067
  if (this.NodeManager.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
1092
1068
  this.NodeManager.LavalinkManager.emit("debug", DebugEvents.PlayerUpdateFilterFixApply, {
@@ -1291,8 +1267,15 @@ export class LavalinkNode {
1291
1267
  }
1292
1268
  this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
1293
1269
  // If there are no songs in the queue
1294
- if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
1295
- return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1270
+ if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
1271
+ 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
1272
+ await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
1273
+ return;
1274
+ }
1275
+ catch {
1276
+ return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
1277
+ }
1278
+ }
1296
1279
  // remove the current track, and enqueue the next one
1297
1280
  await queueTrackEnd(player);
1298
1281
  // if no track available, end queue
@@ -1301,7 +1284,7 @@ export class LavalinkNode {
1301
1284
  }
1302
1285
  // play track if autoSkip is true
1303
1286
  if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
1304
- player.play({ noReplace: true });
1287
+ player.play({ track: player.queue.current, noReplace: false }); // Replace the stuck track with the new track.
1305
1288
  }
1306
1289
  return;
1307
1290
  }
@@ -1332,22 +1315,22 @@ export class LavalinkNode {
1332
1315
  return;
1333
1316
  }
1334
1317
  /** @private util function for handling SponsorBlock Segmentloaded event */
1335
- async SponsorBlockSegmentLoaded(player, track, payload) {
1318
+ SponsorBlockSegmentLoaded(player, track, payload) {
1336
1319
  this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track || this.getTrackOfPayload(payload), payload);
1337
1320
  return;
1338
1321
  }
1339
1322
  /** @private util function for handling SponsorBlock SegmentSkipped event */
1340
- async SponsorBlockSegmentSkipped(player, track, payload) {
1323
+ SponsorBlockSegmentSkipped(player, track, payload) {
1341
1324
  this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track || this.getTrackOfPayload(payload), payload);
1342
1325
  return;
1343
1326
  }
1344
1327
  /** @private util function for handling SponsorBlock Chaptersloaded event */
1345
- async SponsorBlockChaptersLoaded(player, track, payload) {
1328
+ SponsorBlockChaptersLoaded(player, track, payload) {
1346
1329
  this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track || this.getTrackOfPayload(payload), payload);
1347
1330
  return;
1348
1331
  }
1349
1332
  /** @private util function for handling SponsorBlock Chaptersstarted event */
1350
- async SponsorBlockChapterStarted(player, track, payload) {
1333
+ SponsorBlockChapterStarted(player, track, payload) {
1351
1334
  this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track || this.getTrackOfPayload(payload), payload);
1352
1335
  return;
1353
1336
  }
@@ -1473,7 +1456,8 @@ export class LavalinkNode {
1473
1456
  if (player.queue.current) {
1474
1457
  if (payload.type === "TrackEndEvent")
1475
1458
  this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
1476
- return player.play({ noReplace: true, paused: false });
1459
+ if (this.NodeManager.LavalinkManager.options.autoSkip)
1460
+ return player.play({ noReplace: true, paused: false });
1477
1461
  }
1478
1462
  }
1479
1463
  else {
@@ -71,6 +71,9 @@ export class Player {
71
71
  * @param LavalinkManager
72
72
  */
73
73
  constructor(options, LavalinkManager) {
74
+ if (typeof options?.customData === "object")
75
+ for (const [key, value] of Object.entries(options.customData))
76
+ this.set(key, value);
74
77
  this.options = options;
75
78
  this.filterManager = new FilterManager(this);
76
79
  this.LavalinkManager = LavalinkManager;
@@ -264,6 +267,8 @@ export class Player {
264
267
  delete options.clientTrack;
265
268
  if (options && "track" in options)
266
269
  delete options.track;
270
+ // 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
271
+ await queueTrackEnd(this, true);
267
272
  // try to play the next track if possible
268
273
  if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
269
274
  return this.play(options);
@@ -399,6 +404,8 @@ export class Player {
399
404
  const now = performance.now();
400
405
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
401
406
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
407
+ // emit the event
408
+ this.LavalinkManager.emit("playerPaused", this, this.queue.current);
402
409
  return this;
403
410
  }
404
411
  /**
@@ -411,6 +418,8 @@ export class Player {
411
418
  const now = performance.now();
412
419
  await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
413
420
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
421
+ // emit the event
422
+ this.LavalinkManager.emit("playerResumed", this, this.queue.current);
414
423
  return this;
415
424
  }
416
425
  /**
@@ -676,6 +685,7 @@ export class Player {
676
685
  }
677
686
  const data = this.toJSON();
678
687
  const currentTrack = this.queue.current;
688
+ const segments = await this.getSponsorBlock().catch(() => []);
679
689
  const voiceData = this.voice;
680
690
  if (!voiceData.endpoint ||
681
691
  !voiceData.sessionId ||
@@ -700,6 +710,33 @@ export class Player {
700
710
  }
701
711
  });
702
712
  });
713
+ const hasSponsorBlock = this.node.info?.plugins?.find(v => v.name === "sponsorblock-plugin");
714
+ if (hasSponsorBlock) {
715
+ if (segments.length) {
716
+ await this.setSponsorBlock(segments).catch(error => {
717
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
718
+ this.LavalinkManager.emit("debug", DebugEvents.PlayerChangeNode, {
719
+ state: "error",
720
+ error: error,
721
+ message: `Player > changeNode() Unable to set SponsorBlock Segments`,
722
+ functionLayer: "Player > changeNode()",
723
+ });
724
+ }
725
+ });
726
+ }
727
+ else {
728
+ await this.setSponsorBlock().catch(error => {
729
+ if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
730
+ this.LavalinkManager.emit("debug", DebugEvents.PlayerChangeNode, {
731
+ state: "error",
732
+ error: error,
733
+ message: `Player > changeNode() Unable to set SponsorBlock Segments`,
734
+ functionLayer: "Player > changeNode()",
735
+ });
736
+ }
737
+ });
738
+ }
739
+ }
703
740
  if (currentTrack) { // If there is a current track, send it to the new node.
704
741
  await this.node.updatePlayer({
705
742
  guildId: this.guildId,
@@ -713,6 +750,8 @@ export class Player {
713
750
  }
714
751
  });
715
752
  }
753
+ this.paused = data.paused;
754
+ this.playing = data.playing;
716
755
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
717
756
  return this.node.id;
718
757
  }
@@ -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
@@ -73,6 +73,8 @@ export interface PlayerOptions {
73
73
  instaUpdateFiltersFix?: boolean;
74
74
  /** If a volume should be applied via filters instead of lavalink-volume */
75
75
  applyVolumeAsFilter?: boolean;
76
+ /** Custom Data for the player get/set datastorage */
77
+ customData?: anyObject;
76
78
  }
77
79
  export type anyObject = {
78
80
  [key: string | number]: string | number | null | anyObject;
@@ -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" | "ymsearch";
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" | "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
- tracK: LavalinkTrack;
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
- tracK: LavalinkTrack;
101
+ track: LavalinkTrack;
102
102
  }
103
103
  export interface WebSocketClosedEvent extends PlayerEvent {
104
104
  type: "WebSocketClosedEvent";
@@ -20,7 +20,7 @@ export declare function parseLavalinkConnUrl(connectionUrl: string): {
20
20
  port: number;
21
21
  };
22
22
  export declare class ManagerUtils {
23
- LavalinkManager: LavalinkManager | null;
23
+ LavalinkManager: LavalinkManager | undefined;
24
24
  constructor(LavalinkManager?: LavalinkManager);
25
25
  buildPluginInfo(data: any, clientData?: any): any;
26
26
  buildTrack(data: LavalinkTrack | Track, requester: unknown): Track;
@@ -112,4 +112,4 @@ 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>;
@@ -25,7 +25,7 @@ export function parseLavalinkConnUrl(connectionUrl) {
25
25
  };
26
26
  }
27
27
  export class ManagerUtils {
28
- LavalinkManager = null;
28
+ LavalinkManager = undefined;
29
29
  constructor(LavalinkManager) {
30
30
  this.LavalinkManager = LavalinkManager;
31
31
  }
@@ -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)
@@ -421,10 +445,10 @@ export async function queueTrackEnd(player) {
421
445
  // and if repeatMode == queue, add it back to the queue!
422
446
  if (player.repeatMode === "queue" && player.queue.current)
423
447
  player.queue.tracks.push(player.queue.current);
424
- // change the current Track to the next upcoming one
425
- const nextSong = player.queue.tracks.shift();
448
+ // change the current Track to the next upcoming one
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
@@ -53,18 +53,6 @@ export declare class LavalinkNode {
53
53
  * ```
54
54
  */
55
55
  constructor(options: LavalinkNodeOptions, manager: NodeManager);
56
- /**
57
- * Parse url params correctly for lavalink requests, including support for urls and uris.
58
- * @param url input url object
59
- * @param extraQueryUrlParams UrlSearchParams to use in a encodedURI, useful for example for flowertts
60
- * @returns the url as a valid string
61
- *
62
- * @example
63
- * ```ts
64
- * player.node.getRequestingUrl(new URL(`http://localhost:2333/v4/loadtracks?identifier=Never gonna give you up`));
65
- * ```
66
- */
67
- private getRequestingUrl;
68
56
  /**
69
57
  * Raw Request util function
70
58
  * @param endpoint endpoint string
@@ -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
@@ -73,6 +73,8 @@ export interface PlayerOptions {
73
73
  instaUpdateFiltersFix?: boolean;
74
74
  /** If a volume should be applied via filters instead of lavalink-volume */
75
75
  applyVolumeAsFilter?: boolean;
76
+ /** Custom Data for the player get/set datastorage */
77
+ customData?: anyObject;
76
78
  }
77
79
  export type anyObject = {
78
80
  [key: string | number]: string | number | null | anyObject;
@@ -4,7 +4,7 @@ import type { Base64 } from "./Utils";
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" | "ymsearch";
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" | "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
- tracK: LavalinkTrack;
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
- tracK: LavalinkTrack;
101
+ track: LavalinkTrack;
102
102
  }
103
103
  export interface WebSocketClosedEvent extends PlayerEvent {
104
104
  type: "WebSocketClosedEvent";
@@ -20,7 +20,7 @@ export declare function parseLavalinkConnUrl(connectionUrl: string): {
20
20
  port: number;
21
21
  };
22
22
  export declare class ManagerUtils {
23
- LavalinkManager: LavalinkManager | null;
23
+ LavalinkManager: LavalinkManager | undefined;
24
24
  constructor(LavalinkManager?: LavalinkManager);
25
25
  buildPluginInfo(data: any, clientData?: any): any;
26
26
  buildTrack(data: LavalinkTrack | Track, requester: unknown): Track;
@@ -112,4 +112,4 @@ 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>;