lavalink-client 2.5.0 → 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.
- package/dist/cjs/structures/LavalinkManager.js +4 -4
- package/dist/cjs/structures/LavalinkManagerStatics.js +14 -0
- package/dist/cjs/structures/Node.js +10 -3
- package/dist/cjs/structures/Player.js +36 -0
- 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 +1 -1
- package/dist/cjs/structures/Utils.js +29 -5
- package/dist/esm/structures/LavalinkManager.js +4 -4
- package/dist/esm/structures/LavalinkManagerStatics.js +14 -0
- package/dist/esm/structures/Node.js +10 -3
- package/dist/esm/structures/Player.js +36 -0
- 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 +1 -1
- package/dist/esm/structures/Utils.js +29 -5
- 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 +1 -1
- package/package.json +11 -11
|
@@ -495,7 +495,7 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
495
495
|
functionLayer: "LavalinkManager > sendRawData()",
|
|
496
496
|
});
|
|
497
497
|
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
498
|
-
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function,
|
|
498
|
+
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 });
|
|
499
499
|
}
|
|
500
500
|
else {
|
|
501
501
|
await player.node.updatePlayer({
|
|
@@ -505,8 +505,8 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
505
505
|
token: update.token,
|
|
506
506
|
endpoint: update.endpoint,
|
|
507
507
|
sessionId: sessionId2Use,
|
|
508
|
-
}
|
|
509
|
-
}
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
510
|
});
|
|
511
511
|
if (this.options?.advancedOptions?.enableDebugEvents) {
|
|
512
512
|
this.emit("debug", Constants_1.DebugEvents.NoAudioDebug, {
|
|
@@ -516,7 +516,7 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
516
516
|
});
|
|
517
517
|
}
|
|
518
518
|
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
519
|
-
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function,
|
|
519
|
+
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 });
|
|
520
520
|
}
|
|
521
521
|
return;
|
|
522
522
|
}
|
|
@@ -51,6 +51,12 @@ exports.DefaultSources = {
|
|
|
51
51
|
"vkmusic": "vksearch",
|
|
52
52
|
"vk music": "vksearch",
|
|
53
53
|
"vkrec": "vkrec",
|
|
54
|
+
"vk": "vksearch",
|
|
55
|
+
// Qobuz (lavasrc)
|
|
56
|
+
"qbsearch": "qbsearch",
|
|
57
|
+
"qobuz": "qbsearch",
|
|
58
|
+
"qbisrc": "qbisrc",
|
|
59
|
+
"qbrec": "qbrec",
|
|
54
60
|
// speak PLUGIN
|
|
55
61
|
"speak": "speak",
|
|
56
62
|
"tts": "tts",
|
|
@@ -73,6 +79,12 @@ exports.DefaultSources = {
|
|
|
73
79
|
"https": "https",
|
|
74
80
|
"link": "link",
|
|
75
81
|
"uri": "uri",
|
|
82
|
+
// tidal
|
|
83
|
+
"tidal": "tdsearch",
|
|
84
|
+
"td": "tdsearch",
|
|
85
|
+
"tidal music": "tdsearch",
|
|
86
|
+
"tdsearch": "tdsearch",
|
|
87
|
+
"tdrec": "tdrec",
|
|
76
88
|
// jiosaavn
|
|
77
89
|
"jiosaavn": "jssearch",
|
|
78
90
|
"js": "jssearch",
|
|
@@ -125,6 +137,8 @@ exports.SourceLinksRegexes = {
|
|
|
125
137
|
SpotifyAlbumRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?album\/(?<identifier>[a-zA-Z0-9-_]+)/,
|
|
126
138
|
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-_]+)/,
|
|
127
139
|
appleMusic: /https?:\/\/?(?:www\.)?music\.apple\.com\/(\S+)/,
|
|
140
|
+
/** From tidal */
|
|
141
|
+
tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
|
|
128
142
|
/** From jiosaavn-plugin */
|
|
129
143
|
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_\/,]+)/,
|
|
130
144
|
/** FROM DUNCTE BOT PLUGIN */
|
|
@@ -1271,8 +1271,15 @@ class LavalinkNode {
|
|
|
1271
1271
|
}
|
|
1272
1272
|
this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
|
|
1273
1273
|
// If there are no songs in the queue
|
|
1274
|
-
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
1275
|
-
|
|
1274
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying"))) {
|
|
1275
|
+
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
|
|
1276
|
+
await player.node.updatePlayer({ guildId: player.guildId, playerOptions: { track: { encoded: null } } }); //trackEnd -> queueEnd
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
catch {
|
|
1280
|
+
return this.queueEnd(player, track || this.getTrackOfPayload(payload), payload);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1276
1283
|
// remove the current track, and enqueue the next one
|
|
1277
1284
|
await (0, Utils_1.queueTrackEnd)(player);
|
|
1278
1285
|
// if no track available, end queue
|
|
@@ -1281,7 +1288,7 @@ class LavalinkNode {
|
|
|
1281
1288
|
}
|
|
1282
1289
|
// play track if autoSkip is true
|
|
1283
1290
|
if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
|
|
1284
|
-
player.play({ noReplace:
|
|
1291
|
+
player.play({ track: player.queue.current, noReplace: false }); // Replace the stuck track with the new track.
|
|
1285
1292
|
}
|
|
1286
1293
|
return;
|
|
1287
1294
|
}
|
|
@@ -270,6 +270,8 @@ class Player {
|
|
|
270
270
|
delete options.clientTrack;
|
|
271
271
|
if (options && "track" in options)
|
|
272
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 (0, Utils_1.queueTrackEnd)(this, true);
|
|
273
275
|
// try to play the next track if possible
|
|
274
276
|
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
275
277
|
return this.play(options);
|
|
@@ -405,6 +407,8 @@ class Player {
|
|
|
405
407
|
const now = performance.now();
|
|
406
408
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
|
|
407
409
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
410
|
+
// emit the event
|
|
411
|
+
this.LavalinkManager.emit("playerPaused", this, this.queue.current);
|
|
408
412
|
return this;
|
|
409
413
|
}
|
|
410
414
|
/**
|
|
@@ -417,6 +421,8 @@ class Player {
|
|
|
417
421
|
const now = performance.now();
|
|
418
422
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
|
|
419
423
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
424
|
+
// emit the event
|
|
425
|
+
this.LavalinkManager.emit("playerResumed", this, this.queue.current);
|
|
420
426
|
return this;
|
|
421
427
|
}
|
|
422
428
|
/**
|
|
@@ -682,6 +688,7 @@ class Player {
|
|
|
682
688
|
}
|
|
683
689
|
const data = this.toJSON();
|
|
684
690
|
const currentTrack = this.queue.current;
|
|
691
|
+
const segments = await this.getSponsorBlock().catch(() => []);
|
|
685
692
|
const voiceData = this.voice;
|
|
686
693
|
if (!voiceData.endpoint ||
|
|
687
694
|
!voiceData.sessionId ||
|
|
@@ -706,6 +713,33 @@ class Player {
|
|
|
706
713
|
}
|
|
707
714
|
});
|
|
708
715
|
});
|
|
716
|
+
const hasSponsorBlock = this.node.info?.plugins?.find(v => v.name === "sponsorblock-plugin");
|
|
717
|
+
if (hasSponsorBlock) {
|
|
718
|
+
if (segments.length) {
|
|
719
|
+
await this.setSponsorBlock(segments).catch(error => {
|
|
720
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
721
|
+
this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerChangeNode, {
|
|
722
|
+
state: "error",
|
|
723
|
+
error: error,
|
|
724
|
+
message: `Player > changeNode() Unable to set SponsorBlock Segments`,
|
|
725
|
+
functionLayer: "Player > changeNode()",
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
await this.setSponsorBlock().catch(error => {
|
|
732
|
+
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
|
|
733
|
+
this.LavalinkManager.emit("debug", Constants_1.DebugEvents.PlayerChangeNode, {
|
|
734
|
+
state: "error",
|
|
735
|
+
error: error,
|
|
736
|
+
message: `Player > changeNode() Unable to set SponsorBlock Segments`,
|
|
737
|
+
functionLayer: "Player > changeNode()",
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
709
743
|
if (currentTrack) { // If there is a current track, send it to the new node.
|
|
710
744
|
await this.node.updatePlayer({
|
|
711
745
|
guildId: this.guildId,
|
|
@@ -719,6 +753,8 @@ class Player {
|
|
|
719
753
|
}
|
|
720
754
|
});
|
|
721
755
|
}
|
|
756
|
+
this.paused = data.paused;
|
|
757
|
+
this.playing = data.playing;
|
|
722
758
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
723
759
|
return this.node.id;
|
|
724
760
|
}
|
|
@@ -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,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>;
|
|
@@ -307,6 +307,9 @@ class ManagerUtils {
|
|
|
307
307
|
if (LavalinkManagerStatics_1.SourceLinksRegexes.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
|
|
308
308
|
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled");
|
|
309
309
|
}
|
|
310
|
+
if (LavalinkManagerStatics_1.SourceLinksRegexes.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
|
|
311
|
+
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tidal' enabled");
|
|
312
|
+
}
|
|
310
313
|
return;
|
|
311
314
|
}
|
|
312
315
|
transformQuery(query) {
|
|
@@ -371,6 +374,12 @@ class ManagerUtils {
|
|
|
371
374
|
if (source === "speak" && !node.info?.plugins?.find(c => c.name.toLowerCase().includes(LavalinkManagerStatics_1.LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
|
|
372
375
|
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
|
|
373
376
|
}
|
|
377
|
+
if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
378
|
+
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdsearch' work");
|
|
379
|
+
}
|
|
380
|
+
if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
|
|
381
|
+
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdrec' work");
|
|
382
|
+
}
|
|
374
383
|
if (source === "tts" && !node.info?.plugins?.find(c => c.name.toLowerCase().includes(LavalinkManagerStatics_1.LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
|
|
375
384
|
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
|
|
376
385
|
}
|
|
@@ -386,6 +395,21 @@ class ManagerUtils {
|
|
|
386
395
|
if (source === "ytsearch" && !node.info?.sourceManagers?.includes("youtube")) {
|
|
387
396
|
throw new Error("Lavalink Node has not 'youtube' enabled, which is required to have 'ytsearch' work");
|
|
388
397
|
}
|
|
398
|
+
if (source === "vksearch" && !node.info?.sourceManagers?.includes("vkmusic")) {
|
|
399
|
+
throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vksearch' work");
|
|
400
|
+
}
|
|
401
|
+
if (source === "vkrec" && !node.info?.sourceManagers?.includes("vkmusic")) {
|
|
402
|
+
throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vkrec' work");
|
|
403
|
+
}
|
|
404
|
+
if (source === "qbsearch" && !node.info?.sourceManagers?.includes("qobuz")) {
|
|
405
|
+
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbsearch' work");
|
|
406
|
+
}
|
|
407
|
+
if (source === "qbisrc" && !node.info?.sourceManagers?.includes("qobuz")) {
|
|
408
|
+
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbisrc' work");
|
|
409
|
+
}
|
|
410
|
+
if (source === "qbrec" && !node.info?.sourceManagers?.includes("qobuz")) {
|
|
411
|
+
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbrec' work");
|
|
412
|
+
}
|
|
389
413
|
return;
|
|
390
414
|
}
|
|
391
415
|
}
|
|
@@ -418,7 +442,7 @@ class MiniMap extends Map {
|
|
|
418
442
|
}
|
|
419
443
|
}
|
|
420
444
|
exports.MiniMap = MiniMap;
|
|
421
|
-
async function queueTrackEnd(player) {
|
|
445
|
+
async function queueTrackEnd(player, dontShiftQueue = false) {
|
|
422
446
|
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.
|
|
423
447
|
player.queue.previous.unshift(player.queue.current);
|
|
424
448
|
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
@@ -428,10 +452,10 @@ async function queueTrackEnd(player) {
|
|
|
428
452
|
// and if repeatMode == queue, add it back to the queue!
|
|
429
453
|
if (player.repeatMode === "queue" && player.queue.current)
|
|
430
454
|
player.queue.tracks.push(player.queue.current);
|
|
431
|
-
// change the current Track to the next upcoming one
|
|
432
|
-
const nextSong = player.queue.tracks.shift();
|
|
455
|
+
// change the current Track to the next upcoming one
|
|
456
|
+
const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
|
|
433
457
|
try {
|
|
434
|
-
if (player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
458
|
+
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong))
|
|
435
459
|
await nextSong.resolve(player);
|
|
436
460
|
player.queue.current = nextSong || null;
|
|
437
461
|
// save it in the DB
|
|
@@ -448,7 +472,7 @@ async function queueTrackEnd(player) {
|
|
|
448
472
|
}
|
|
449
473
|
player.LavalinkManager.emit("trackError", player, player.queue.current, error);
|
|
450
474
|
// try to play the next track if possible
|
|
451
|
-
if (player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
475
|
+
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0])
|
|
452
476
|
return queueTrackEnd(player);
|
|
453
477
|
}
|
|
454
478
|
// return the new current Track
|
|
@@ -492,7 +492,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
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,8 +502,8 @@ 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, {
|
|
@@ -513,7 +513,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
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
|
}
|
|
@@ -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,6 +134,8 @@ 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
140
|
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_\/,]+)/,
|
|
127
141
|
/** FROM DUNCTE BOT PLUGIN */
|
|
@@ -1267,8 +1267,15 @@ export class LavalinkNode {
|
|
|
1267
1267
|
}
|
|
1268
1268
|
this.NodeManager.LavalinkManager.emit("trackStuck", player, track || this.getTrackOfPayload(payload), payload);
|
|
1269
1269
|
// If there are no songs in the queue
|
|
1270
|
-
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
1271
|
-
|
|
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
|
+
}
|
|
1272
1279
|
// remove the current track, and enqueue the next one
|
|
1273
1280
|
await queueTrackEnd(player);
|
|
1274
1281
|
// if no track available, end queue
|
|
@@ -1277,7 +1284,7 @@ export class LavalinkNode {
|
|
|
1277
1284
|
}
|
|
1278
1285
|
// play track if autoSkip is true
|
|
1279
1286
|
if (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) {
|
|
1280
|
-
player.play({ noReplace:
|
|
1287
|
+
player.play({ track: player.queue.current, noReplace: false }); // Replace the stuck track with the new track.
|
|
1281
1288
|
}
|
|
1282
1289
|
return;
|
|
1283
1290
|
}
|
|
@@ -267,6 +267,8 @@ export class Player {
|
|
|
267
267
|
delete options.clientTrack;
|
|
268
268
|
if (options && "track" in options)
|
|
269
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);
|
|
270
272
|
// try to play the next track if possible
|
|
271
273
|
if (this.LavalinkManager.options?.autoSkipOnResolveError === true && this.queue.tracks[0])
|
|
272
274
|
return this.play(options);
|
|
@@ -402,6 +404,8 @@ export class Player {
|
|
|
402
404
|
const now = performance.now();
|
|
403
405
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
|
|
404
406
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
407
|
+
// emit the event
|
|
408
|
+
this.LavalinkManager.emit("playerPaused", this, this.queue.current);
|
|
405
409
|
return this;
|
|
406
410
|
}
|
|
407
411
|
/**
|
|
@@ -414,6 +418,8 @@ export class Player {
|
|
|
414
418
|
const now = performance.now();
|
|
415
419
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
|
|
416
420
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
421
|
+
// emit the event
|
|
422
|
+
this.LavalinkManager.emit("playerResumed", this, this.queue.current);
|
|
417
423
|
return this;
|
|
418
424
|
}
|
|
419
425
|
/**
|
|
@@ -679,6 +685,7 @@ export class Player {
|
|
|
679
685
|
}
|
|
680
686
|
const data = this.toJSON();
|
|
681
687
|
const currentTrack = this.queue.current;
|
|
688
|
+
const segments = await this.getSponsorBlock().catch(() => []);
|
|
682
689
|
const voiceData = this.voice;
|
|
683
690
|
if (!voiceData.endpoint ||
|
|
684
691
|
!voiceData.sessionId ||
|
|
@@ -703,6 +710,33 @@ export class Player {
|
|
|
703
710
|
}
|
|
704
711
|
});
|
|
705
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
|
+
}
|
|
706
740
|
if (currentTrack) { // If there is a current track, send it to the new node.
|
|
707
741
|
await this.node.updatePlayer({
|
|
708
742
|
guildId: this.guildId,
|
|
@@ -716,6 +750,8 @@ export class Player {
|
|
|
716
750
|
}
|
|
717
751
|
});
|
|
718
752
|
}
|
|
753
|
+
this.paused = data.paused;
|
|
754
|
+
this.playing = data.playing;
|
|
719
755
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
720
756
|
return this.node.id;
|
|
721
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
|
|
@@ -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,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>;
|
|
@@ -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
|
|
@@ -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";
|
|
|
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,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>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lavalink-client",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -58,19 +58,19 @@
|
|
|
58
58
|
},
|
|
59
59
|
"homepage": "https://tomato6966.github.io/lavalink-client/",
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@eslint/eslintrc": "^3.
|
|
62
|
-
"@eslint/js": "^9.
|
|
63
|
-
"@types/node": "^22.
|
|
64
|
-
"@types/ws": "^8.
|
|
65
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
66
|
-
"@typescript-eslint/parser": "^8.
|
|
67
|
-
"eslint": "^9.
|
|
68
|
-
"tsc-alias": "^1.8.
|
|
69
|
-
"typescript": "^5.
|
|
61
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
62
|
+
"@eslint/js": "^9.25.0",
|
|
63
|
+
"@types/node": "^22.14.1",
|
|
64
|
+
"@types/ws": "^8.18.1",
|
|
65
|
+
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
|
66
|
+
"@typescript-eslint/parser": "^8.30.1",
|
|
67
|
+
"eslint": "^9.25.0",
|
|
68
|
+
"tsc-alias": "^1.8.15",
|
|
69
|
+
"typescript": "^5.8.3"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"tslib": "^2.8.1",
|
|
73
|
-
"ws": "^8.18.
|
|
73
|
+
"ws": "^8.18.1"
|
|
74
74
|
},
|
|
75
75
|
"engines": {
|
|
76
76
|
"node": ">=18.0.0",
|