lavalink-client 1.1.25 → 1.2.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/README.md +39 -1
- package/dist/cjs/structures/CustomSearches/BandCampSearch.js +1 -0
- package/dist/cjs/structures/LavalinkManager.d.ts +51 -12
- package/dist/cjs/structures/LavalinkManager.js +21 -14
- package/dist/cjs/structures/Node.d.ts +11 -2
- package/dist/cjs/structures/Node.js +111 -32
- package/dist/cjs/structures/Player.d.ts +15 -2
- package/dist/cjs/structures/Player.js +56 -11
- package/dist/cjs/structures/Queue.d.ts +1 -1
- package/dist/cjs/structures/Utils.d.ts +56 -2
- package/dist/cjs/structures/Utils.js +32 -1
- package/dist/esm/structures/CustomSearches/BandCampSearch.js +1 -0
- package/dist/esm/structures/LavalinkManager.d.ts +51 -12
- package/dist/esm/structures/LavalinkManager.js +21 -14
- package/dist/esm/structures/Node.d.ts +11 -2
- package/dist/esm/structures/Node.js +110 -31
- package/dist/esm/structures/Player.d.ts +15 -2
- package/dist/esm/structures/Player.js +56 -11
- package/dist/esm/structures/Queue.d.ts +1 -1
- package/dist/esm/structures/Utils.d.ts +56 -2
- package/dist/esm/structures/Utils.js +30 -0
- package/dist/types/structures/LavalinkManager.d.ts +51 -12
- package/dist/types/structures/Node.d.ts +11 -2
- package/dist/types/structures/Player.d.ts +15 -2
- package/dist/types/structures/Queue.d.ts +1 -1
- package/dist/types/structures/Utils.d.ts +56 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,6 +82,10 @@ Check out the [Documentation](https://lc4.gitbook.io/lavalink-client) for **Exam
|
|
|
82
82
|
- 🛡️ Lavalink Validations
|
|
83
83
|
- It only let's you use the filters / plugins / sources, if Lavalink actually has it enabled
|
|
84
84
|
|
|
85
|
+
- 🛡️ Client Validations
|
|
86
|
+
- Allows you to whitelist links and even blacklist links / words / domain names, so that it doesn't allow requests you don't want!
|
|
87
|
+
- Checks almost all Lavalink Requests for out of bound errors, right before the request is made to prevent process breaking errors.
|
|
88
|
+
|
|
85
89
|
- 🧑💻 Memory friendly and easy style
|
|
86
90
|
- Only the required data is displayed, and the store-way & types match Lavalink#IMPLEMENTATION.md
|
|
87
91
|
|
|
@@ -91,4 +95,38 @@ Check out the [Documentation](https://lc4.gitbook.io/lavalink-client) for **Exam
|
|
|
91
95
|
- Pauses / resumes the player if it get's muted / unmuted (server-wide) [soon]
|
|
92
96
|
- ...
|
|
93
97
|
|
|
94
|
-
- 😁 Much much more!
|
|
98
|
+
- 😁 Much much more!
|
|
99
|
+
|
|
100
|
+
# UpdateLog
|
|
101
|
+
|
|
102
|
+
## **Version 1.2.0**
|
|
103
|
+
- Added `player.stopPlaying()`: When executed it **clears the Queue** and **stops playing**, **without destroying the Player**
|
|
104
|
+
- Adjusted `Player.skip()`
|
|
105
|
+
- Added `throwError` Property to: `player.skip(skipTo?:number = 0, throwError?:boolean = true)`.
|
|
106
|
+
- If throwError = false, and no more tracks are in the queue, it won't throw an error and "ignore it". same thing as stopPlaying.
|
|
107
|
+
- Added all Events and Methods from the [SponsorBlock Plugin](https://github.com/topi314/Sponsorblock-Plugin).
|
|
108
|
+
- It also validates if the plugin is in the bot, in order so that you can use the functions:
|
|
109
|
+
- `player.getSponsorBlock()` / `node.getSponsorBlock()`
|
|
110
|
+
- `player.setSponsorBlock(segments:SponsorBlockSegment[])` / `node.setSponsorBlock(segments:SponsorBlockSegment[])`
|
|
111
|
+
- `player.deleteSponsorBlock()` / `node.deleteSponsorBlock()`
|
|
112
|
+
- That Plugin adds following **Events** to the **Manager**: `"SegmentsLoaded"`, `"SegmentSkipped"`, `"ChapterStarted"`, `"ChaptersLoaded"`
|
|
113
|
+
- Example Bot show example in autoplayFunction how to "disable" / "enable" Autoplay with bot data variables.
|
|
114
|
+
- Added `ManagerOptions#emitNewSongsOnly`. If set to true, it won't emit "trackStart" Event, when track.loop is active, or the new current track == the previous (current) track.
|
|
115
|
+
- Added `ManagerOptions#linksBlacklist` which allows user to specify an array of regExp / strings to match query strings (for links / words) and if a match happens it doesn't allow the request (blacklist)
|
|
116
|
+
- Added `ManagerOptions#linksWhitelist` which allows user to specify an array of regExp / strings to match query strings (for links only) and if a match does NOT HAPPEN it doesn't allow the request (whitelist)
|
|
117
|
+
- Added `ManagerOptions#linksAllowed` if set to false, it does not allow requests which are links
|
|
118
|
+
- Moved `ManaagerOptions#debugOptions` to `ManaagerOptions#advancedOptions.debugOptions`
|
|
119
|
+
|
|
120
|
+
### **Version 1.2.1**
|
|
121
|
+
- Adjusted `player.stopPlaying()`
|
|
122
|
+
- There are now following parameters. `stopPlaying(clearQueue:boolean = true, executeAutoplay:boolean = false)`.
|
|
123
|
+
- On Default it now clears the queue and stops playing. Also it does not execute Autoplay on default. IF you want the function to behave differently, you can use the 2 states for that.
|
|
124
|
+
- Fixed that it looped the current track if repeatmode === "track" / "queue". (it stops playing and loop stays)
|
|
125
|
+
- Implemented a `parseLavalinkConnUrl(connectionUrl:string)` Util Function.
|
|
126
|
+
- It allows you to parse Lavalink Connection Data of a Lavalink Connection Url.
|
|
127
|
+
Pattern: `lavalink://<nodeId>:<nodeAuthorization(Password)>@<NodeHost>:<NodePort>`
|
|
128
|
+
- Note that the nodeId and NodeAuthorization must be encoded via encodeURIComponents before you provide it into the function.
|
|
129
|
+
- The function will return the following: `{ id: string, authorization: string, host: string, port: number }`
|
|
130
|
+
- Example: `parseLavalinkConnUrl("lavalink://LavalinkNode_1:strong%23password1@localhost:2345")` will give you:
|
|
131
|
+
`{ id: "LavalinkNode_1", authorization: "strong#password1", host: "localhost", port: 2345 }`
|
|
132
|
+
- Note that the password "strong#password1" when encoded turns into "strong%23password1". For more information check the example bot
|
|
@@ -5,6 +5,7 @@ const undici_1 = require("undici");
|
|
|
5
5
|
const bandCampSearch = async (player, query, requestUser) => {
|
|
6
6
|
let error = null;
|
|
7
7
|
let tracks = [];
|
|
8
|
+
player.LavalinkManager.utils.validateQueryString(player.node, query);
|
|
8
9
|
try {
|
|
9
10
|
const data = await (0, undici_1.fetch)(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
|
|
10
11
|
headers: {
|
|
@@ -5,7 +5,7 @@ import { NodeManager } from "./NodeManager";
|
|
|
5
5
|
import { DestroyReasonsType, Player, PlayerJson, PlayerOptions } from "./Player";
|
|
6
6
|
import { ManagerQueueOptions } from "./Queue";
|
|
7
7
|
import { Track, UnresolvedTrack } from "./Track";
|
|
8
|
-
import { ChannelDeletePacket, GuildShardPayload, ManagerUtils, MiniMap, SearchPlatform, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, VoicePacket, VoiceServer, VoiceState, WebSocketClosedEvent } from "./Utils";
|
|
8
|
+
import { ChannelDeletePacket, GuildShardPayload, ManagerUtils, MiniMap, SearchPlatform, SponsorBlockChaptersLoaded, SponsorBlockChapterStarted, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, VoicePacket, VoiceServer, VoiceState, WebSocketClosedEvent } from "./Utils";
|
|
9
9
|
export interface LavalinkManager {
|
|
10
10
|
nodeManager: NodeManager;
|
|
11
11
|
utils: ManagerUtils;
|
|
@@ -33,7 +33,7 @@ export interface ManagerPlayerOptions {
|
|
|
33
33
|
onDisconnect?: {
|
|
34
34
|
/** Try to reconnect? -> If fails -> Destroy */
|
|
35
35
|
autoReconnect?: boolean;
|
|
36
|
-
/** Instantly destroy player (overrides autoReconnect) */
|
|
36
|
+
/** Instantly destroy player (overrides autoReconnect) | Don't provide == disable feature*/
|
|
37
37
|
destroyPlayer?: boolean;
|
|
38
38
|
};
|
|
39
39
|
onEmptyQueue?: {
|
|
@@ -56,16 +56,27 @@ export interface ManagerOptions {
|
|
|
56
56
|
playerOptions?: ManagerPlayerOptions;
|
|
57
57
|
/** If it should skip to the next Track on TrackEnd / TrackError etc. events */
|
|
58
58
|
autoSkip?: boolean;
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
/** If it should emit only new (unique) songs and not when a looping track (or similar) is plaid, default false */
|
|
60
|
+
emitNewSongsOnly?: boolean;
|
|
61
|
+
/** Only allow link requests with links either matching some of that regExp or including some of that string */
|
|
62
|
+
linksWhitelist?: (RegExp | string)[];
|
|
63
|
+
/** Never allow link requests with links either matching some of that regExp or including some of that string (doesn't even allow if it's whitelisted) */
|
|
64
|
+
linksBlacklist?: (RegExp | string)[];
|
|
65
|
+
/** If links should be allowed or not. If set to false, it will throw an error if a link was provided. */
|
|
66
|
+
linksAllowed?: boolean;
|
|
67
|
+
/** Advanced Options for the Library, which may or may not be "library breaking" */
|
|
68
|
+
advancedOptions?: {
|
|
69
|
+
/** optional */
|
|
70
|
+
debugOptions?: {
|
|
71
|
+
/** logs for debugging the "no-Audio" playing error */
|
|
72
|
+
noAudio?: boolean;
|
|
73
|
+
/** For Logging the Destroy function */
|
|
74
|
+
playerDestroy?: {
|
|
75
|
+
/** To show the debug reason at all times. */
|
|
76
|
+
debugLog?: boolean;
|
|
77
|
+
/** If you get 'Error: Use Player#destroy("reason") not LavalinkManager#deletePlayer() to stop the Player' put it on true */
|
|
78
|
+
dontThrowError?: boolean;
|
|
79
|
+
};
|
|
69
80
|
};
|
|
70
81
|
};
|
|
71
82
|
}
|
|
@@ -125,6 +136,34 @@ interface LavalinkManagerEvents {
|
|
|
125
136
|
* @event Manager#playerUpdate
|
|
126
137
|
*/
|
|
127
138
|
"playerUpdate": (oldPlayerJson: PlayerJson, newPlayer: Player) => void;
|
|
139
|
+
/**
|
|
140
|
+
* SPONSORBLOCK-PLUGIN EVENT
|
|
141
|
+
* Emitted when Segments are loaded
|
|
142
|
+
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentsloaded
|
|
143
|
+
* @event Manager#trackError
|
|
144
|
+
*/
|
|
145
|
+
"SegmentsLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentsLoaded) => void;
|
|
146
|
+
/**
|
|
147
|
+
* SPONSORBLOCK-PLUGIN EVENT
|
|
148
|
+
* Emitted when a specific Segment was skipped
|
|
149
|
+
* @link https://github.com/topi314/Sponsorblock-Plugin#segmentskipped
|
|
150
|
+
* @event Manager#trackError
|
|
151
|
+
*/
|
|
152
|
+
"SegmentSkipped": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentSkipped) => void;
|
|
153
|
+
/**
|
|
154
|
+
* SPONSORBLOCK-PLUGIN EVENT
|
|
155
|
+
* Emitted when a specific Chapter starts playing
|
|
156
|
+
* @link https://github.com/topi314/Sponsorblock-Plugin#chapterstarted
|
|
157
|
+
* @event Manager#trackError
|
|
158
|
+
*/
|
|
159
|
+
"ChapterStarted": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChapterStarted) => void;
|
|
160
|
+
/**
|
|
161
|
+
* SPONSORBLOCK-PLUGIN EVENT
|
|
162
|
+
* Emitted when Chapters are loaded
|
|
163
|
+
* @link https://github.com/topi314/Sponsorblock-Plugin#chaptersloaded
|
|
164
|
+
* @event Manager#trackError
|
|
165
|
+
*/
|
|
166
|
+
"ChaptersLoaded": (player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChaptersLoaded) => void;
|
|
128
167
|
}
|
|
129
168
|
export interface LavalinkManager {
|
|
130
169
|
options: ManagerOptions;
|
|
@@ -37,17 +37,22 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
37
37
|
requesterTransformer: options?.playerOptions?.requesterTransformer ?? null,
|
|
38
38
|
useUnresolvedData: options?.playerOptions?.useUnresolvedData ?? false,
|
|
39
39
|
},
|
|
40
|
+
linksWhitelist: options?.linksWhitelist ?? [],
|
|
41
|
+
linksBlacklist: options?.linksBlacklist ?? [],
|
|
40
42
|
autoSkip: options?.autoSkip ?? true,
|
|
43
|
+
emitNewSongsOnly: options?.emitNewSongsOnly ?? false,
|
|
41
44
|
queueOptions: {
|
|
42
45
|
maxPreviousTracks: options?.queueOptions?.maxPreviousTracks ?? 25,
|
|
43
46
|
queueChangesWatcher: options?.queueOptions?.queueChangesWatcher ?? null,
|
|
44
47
|
queueStore: options?.queueOptions?.queueStore ?? new Queue_1.DefaultQueueStore(),
|
|
45
48
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
advancedOptions: {
|
|
50
|
+
debugOptions: {
|
|
51
|
+
noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
|
|
52
|
+
playerDestroy: {
|
|
53
|
+
dontThrowError: options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError ?? false,
|
|
54
|
+
debugLog: options?.advancedOptions?.debugOptions?.playerDestroy?.debugLog ?? false,
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
}
|
|
53
58
|
};
|
|
@@ -60,6 +65,8 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
60
65
|
// if(typeof options?.client !== "object" || typeof options?.client.id !== "string") throw new SyntaxError("ManagerOption.client = { id: string, username?:string } was not provided, which is required");
|
|
61
66
|
if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
|
|
62
67
|
throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
|
|
68
|
+
if (options?.emitNewSongsOnly && typeof options?.emitNewSongsOnly !== "boolean")
|
|
69
|
+
throw new SyntaxError("ManagerOption.emitNewSongsOnly must be either false | true aka boolean");
|
|
63
70
|
if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every(node => this.utils.isNodeOptions(node)))
|
|
64
71
|
throw new SyntaxError("ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node");
|
|
65
72
|
/* QUEUE STORE */
|
|
@@ -113,7 +120,7 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
113
120
|
return;
|
|
114
121
|
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
115
122
|
if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
116
|
-
if (!this.options?.debugOptions?.playerDestroy?.dontThrowError)
|
|
123
|
+
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
117
124
|
throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
|
|
118
125
|
else
|
|
119
126
|
console.error("Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player", oldPlayer.toJSON?.());
|
|
@@ -159,12 +166,12 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
159
166
|
*/
|
|
160
167
|
async sendRawData(data) {
|
|
161
168
|
if (!this.initiated) {
|
|
162
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
169
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
163
170
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet");
|
|
164
171
|
return;
|
|
165
172
|
}
|
|
166
173
|
if (!("t" in data)) {
|
|
167
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
174
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
168
175
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:", data);
|
|
169
176
|
return;
|
|
170
177
|
}
|
|
@@ -181,23 +188,23 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
181
188
|
if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
|
|
182
189
|
const update = ("d" in data ? data.d : data);
|
|
183
190
|
if (!update) {
|
|
184
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
191
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
185
192
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:", data);
|
|
186
193
|
return;
|
|
187
194
|
}
|
|
188
195
|
if (!("token" in update) && !("session_id" in update)) {
|
|
189
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
196
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
190
197
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:", data);
|
|
191
198
|
return;
|
|
192
199
|
}
|
|
193
200
|
const player = this.getPlayer(update.guild_id);
|
|
194
201
|
if (!player) {
|
|
195
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
202
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
196
203
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:", update);
|
|
197
204
|
return;
|
|
198
205
|
}
|
|
199
206
|
if (player.get("internal_destroystatus") === true) {
|
|
200
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
207
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
201
208
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Player is in a destroying state. can't signal the voice states");
|
|
202
209
|
return;
|
|
203
210
|
}
|
|
@@ -214,13 +221,13 @@ class LavalinkManager extends events_1.EventEmitter {
|
|
|
214
221
|
}
|
|
215
222
|
}
|
|
216
223
|
});
|
|
217
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
224
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
218
225
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Sent updatePlayer for voice token session", { voice: { token: update.token, endpoint: update.endpoint, sessionId: player.voice?.sessionId, } });
|
|
219
226
|
return;
|
|
220
227
|
}
|
|
221
228
|
/* voice state update */
|
|
222
229
|
if (update.user_id !== this.options?.client.id) {
|
|
223
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
230
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
224
231
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, voice update user is not equal to provided client id of the manageroptions#client#id", "user:", update.user_id, "manager client id:", this.options?.client.id);
|
|
225
232
|
return;
|
|
226
233
|
}
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import internal from "stream";
|
|
3
3
|
import { Dispatcher, Pool } from "undici";
|
|
4
4
|
import { NodeManager } from "./NodeManager";
|
|
5
|
-
import { DestroyReasonsType } from "./Player";
|
|
5
|
+
import { DestroyReasonsType, Player } from "./Player";
|
|
6
6
|
import { Track } from "./Track";
|
|
7
7
|
import { Base64, InvalidLavalinkRestRequest, LavalinkPlayer, LavaSearchQuery, LavaSearchResponse, PlayerUpdateInfo, RoutePlanner, SearchQuery, SearchResult, Session } from "./Utils";
|
|
8
8
|
/** Modifies any outgoing REST requests. */
|
|
9
9
|
export type ModifyRequest = (options: Dispatcher.RequestOptions) => void;
|
|
10
|
+
export declare const validSponsorBlocks: string[];
|
|
11
|
+
export type SponsorBlockSegment = "sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "filler";
|
|
10
12
|
export interface LavalinkNodeOptions {
|
|
11
13
|
/** The Lavalink Server-Ip / Domain-URL */
|
|
12
14
|
host: string;
|
|
@@ -240,8 +242,15 @@ export declare class LavalinkNode {
|
|
|
240
242
|
private handleEvent;
|
|
241
243
|
private trackStart;
|
|
242
244
|
private trackEnd;
|
|
243
|
-
private queueEnd;
|
|
244
245
|
private trackStuck;
|
|
245
246
|
private trackError;
|
|
246
247
|
private socketClosed;
|
|
248
|
+
private SponsorBlockSegmentLoaded;
|
|
249
|
+
private SponsorBlockSegmentkipped;
|
|
250
|
+
private SponsorBlockChaptersLoaded;
|
|
251
|
+
private SponsorBlockChapterStarted;
|
|
252
|
+
getSponsorBlock(player: Player): Promise<SponsorBlockSegment[]>;
|
|
253
|
+
setSponsorBlock(player: Player, segments?: SponsorBlockSegment[]): Promise<void>;
|
|
254
|
+
deleteSponsorBlock(player: Player): Promise<void>;
|
|
255
|
+
private queueEnd;
|
|
247
256
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LavalinkNode = void 0;
|
|
3
|
+
exports.LavalinkNode = exports.validSponsorBlocks = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
const undici_1 = require("undici");
|
|
7
7
|
const ws_1 = tslib_1.__importDefault(require("ws"));
|
|
8
8
|
const Player_1 = require("./Player");
|
|
9
9
|
const Utils_1 = require("./Utils");
|
|
10
|
+
exports.validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
|
|
10
11
|
class LavalinkNode {
|
|
11
12
|
/** The provided Options of the Node */
|
|
12
13
|
options;
|
|
@@ -98,9 +99,8 @@ class LavalinkNode {
|
|
|
98
99
|
}
|
|
99
100
|
async search(query, requestUser) {
|
|
100
101
|
const Query = this.NodeManager.LavalinkManager.utils.transformQuery(query);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
else if (Query.source)
|
|
102
|
+
this.NodeManager.LavalinkManager.utils.validateQueryString(this, Query.query);
|
|
103
|
+
if (Query.source)
|
|
104
104
|
this.NodeManager.LavalinkManager.utils.validateSourceString(this, Query.source);
|
|
105
105
|
if (["bcsearch", "bandcamp"].includes(Query.source)) {
|
|
106
106
|
throw new Error("Bandcamp Search only works on the player!");
|
|
@@ -566,6 +566,7 @@ class LavalinkNode {
|
|
|
566
566
|
return;
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
|
+
// LAVALINK EVENT HANDLING UTIL FUNCTION
|
|
569
570
|
async handleEvent(payload) {
|
|
570
571
|
if (!payload.guildId)
|
|
571
572
|
return;
|
|
@@ -588,20 +589,36 @@ class LavalinkNode {
|
|
|
588
589
|
case "WebSocketClosedEvent":
|
|
589
590
|
this.socketClosed(player, payload);
|
|
590
591
|
break;
|
|
592
|
+
case "SegmentsLoaded":
|
|
593
|
+
this.SponsorBlockSegmentLoaded(player, player.queue.current, payload);
|
|
594
|
+
break;
|
|
595
|
+
case "SegmentSkipped":
|
|
596
|
+
this.SponsorBlockSegmentkipped(player, player.queue.current, payload);
|
|
597
|
+
break;
|
|
598
|
+
case "ChaptersLoaded":
|
|
599
|
+
this.SponsorBlockChaptersLoaded(player, player.queue.current, payload);
|
|
600
|
+
break;
|
|
601
|
+
case "ChapterStarted":
|
|
602
|
+
this.SponsorBlockChapterStarted(player, player.queue.current, payload);
|
|
603
|
+
break;
|
|
591
604
|
default:
|
|
592
605
|
this.NodeManager.emit("error", this, new Error(`Node#event unknown event '${payload.type}'.`), payload);
|
|
593
606
|
break;
|
|
594
607
|
}
|
|
595
608
|
return;
|
|
596
609
|
}
|
|
610
|
+
// LAVALINK EVENT HANDLING FUNCTIONS
|
|
597
611
|
trackStart(player, track, payload) {
|
|
598
612
|
player.playing = true;
|
|
599
613
|
player.paused = false;
|
|
614
|
+
// don't emit the event if previous track == new track aka track loop
|
|
615
|
+
if (this.NodeManager.LavalinkManager.options?.emitNewSongsOnly === true && player.queue.previous[0]?.info?.identifier === track?.info?.identifier)
|
|
616
|
+
return;
|
|
600
617
|
return this.NodeManager.LavalinkManager.emit("trackStart", player, track, payload);
|
|
601
618
|
}
|
|
602
619
|
async trackEnd(player, track, payload) {
|
|
603
620
|
// If there are no songs in the queue
|
|
604
|
-
if (!player.queue.tracks.length && player.repeatMode === "off")
|
|
621
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
605
622
|
return this.queueEnd(player, track, payload);
|
|
606
623
|
// If a track was forcibly played
|
|
607
624
|
if (payload.reason === "replaced")
|
|
@@ -620,6 +637,12 @@ class LavalinkNode {
|
|
|
620
637
|
// remove tracks from the queue
|
|
621
638
|
if (player.repeatMode !== "track")
|
|
622
639
|
await (0, Utils_1.queueTrackEnd)(player);
|
|
640
|
+
else if (player.queue.current) { // If there was a current Track already and repeatmode === true, add it to the queue.
|
|
641
|
+
player.queue.previous.unshift(player.queue.current);
|
|
642
|
+
if (player.queue.previous.length > player.queue.options.maxPreviousTracks)
|
|
643
|
+
player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
|
|
644
|
+
await player.queue.utils.save();
|
|
645
|
+
}
|
|
623
646
|
// if no track available, end queue
|
|
624
647
|
if (!player.queue.current)
|
|
625
648
|
return this.queueEnd(player, track, payload);
|
|
@@ -628,11 +651,92 @@ class LavalinkNode {
|
|
|
628
651
|
// play track if autoSkip is true
|
|
629
652
|
return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
|
|
630
653
|
}
|
|
654
|
+
async trackStuck(player, track, payload) {
|
|
655
|
+
this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
|
|
656
|
+
// If there are no songs in the queue
|
|
657
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
658
|
+
return this.queueEnd(player, track, payload);
|
|
659
|
+
// remove the current track, and enqueue the next one
|
|
660
|
+
await (0, Utils_1.queueTrackEnd)(player);
|
|
661
|
+
// if no track available, end queue
|
|
662
|
+
if (!player.queue.current)
|
|
663
|
+
return this.queueEnd(player, track, payload);
|
|
664
|
+
// play track if autoSkip is true
|
|
665
|
+
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
666
|
+
}
|
|
667
|
+
async trackError(player, track, payload) {
|
|
668
|
+
this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
|
|
669
|
+
// If there are no songs in the queue
|
|
670
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
671
|
+
return this.queueEnd(player, track, payload);
|
|
672
|
+
// remove the current track, and enqueue the next one
|
|
673
|
+
await (0, Utils_1.queueTrackEnd)(player);
|
|
674
|
+
// if no track available, end queue
|
|
675
|
+
if (!player.queue.current)
|
|
676
|
+
return this.queueEnd(player, track, payload);
|
|
677
|
+
// play track if autoSkip is true
|
|
678
|
+
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
679
|
+
}
|
|
680
|
+
socketClosed(player, payload) {
|
|
681
|
+
return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
|
|
682
|
+
}
|
|
683
|
+
// SPONSOR BLOCK EVENT FUNCTIONS
|
|
684
|
+
SponsorBlockSegmentLoaded(player, track, payload) {
|
|
685
|
+
return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track, payload);
|
|
686
|
+
}
|
|
687
|
+
SponsorBlockSegmentkipped(player, track, payload) {
|
|
688
|
+
return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track, payload);
|
|
689
|
+
}
|
|
690
|
+
SponsorBlockChaptersLoaded(player, track, payload) {
|
|
691
|
+
return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track, payload);
|
|
692
|
+
}
|
|
693
|
+
SponsorBlockChapterStarted(player, track, payload) {
|
|
694
|
+
return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track, payload);
|
|
695
|
+
}
|
|
696
|
+
// SPONSOR BLOCK EXECUTE FUNCTIONS
|
|
697
|
+
async getSponsorBlock(player) {
|
|
698
|
+
// no plugin enabled
|
|
699
|
+
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
700
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
701
|
+
// do the request
|
|
702
|
+
return await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`);
|
|
703
|
+
}
|
|
704
|
+
async setSponsorBlock(player, segments = ["sponsor", "selfpromo"]) {
|
|
705
|
+
// no plugin enabled
|
|
706
|
+
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
707
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
708
|
+
// no segments length
|
|
709
|
+
if (!segments.length)
|
|
710
|
+
throw new RangeError("No Segments provided. Did you ment to use 'deleteSponsorBlock'?");
|
|
711
|
+
// a not valid segment
|
|
712
|
+
if (segments.some(v => !exports.validSponsorBlocks.includes(v.toLowerCase())))
|
|
713
|
+
throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${exports.validSponsorBlocks.map(v => `'${v}'`).join(", ")}`);
|
|
714
|
+
// do the request
|
|
715
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (request) => {
|
|
716
|
+
request.method = "PUT";
|
|
717
|
+
request.body = JSON.stringify(segments.map(v => v.toLowerCase()));
|
|
718
|
+
return request;
|
|
719
|
+
});
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
async deleteSponsorBlock(player) {
|
|
723
|
+
// no plugin enabled
|
|
724
|
+
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
725
|
+
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
726
|
+
// do the request
|
|
727
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (request) => {
|
|
728
|
+
request.method = "DELETE";
|
|
729
|
+
return request;
|
|
730
|
+
});
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
// UTIL FOR QUEUE END
|
|
631
734
|
async queueEnd(player, track, payload) {
|
|
632
735
|
// add previous track to the queue!
|
|
633
736
|
player.queue.current = null;
|
|
634
737
|
player.playing = false;
|
|
635
|
-
|
|
738
|
+
player.set("internal_stopPlaying", undefined);
|
|
739
|
+
if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function" && typeof player.get("internal_autoplayStopPlaying") === "undefined") {
|
|
636
740
|
await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
637
741
|
if (player.queue.tracks.length > 0)
|
|
638
742
|
await (0, Utils_1.queueTrackEnd)(player);
|
|
@@ -642,6 +746,7 @@ class LavalinkNode {
|
|
|
642
746
|
return player.play({ noReplace: true, paused: false });
|
|
643
747
|
}
|
|
644
748
|
}
|
|
749
|
+
player.set("internal_autoplayStopPlaying", undefined);
|
|
645
750
|
player.queue.previous.unshift(track);
|
|
646
751
|
if (payload?.reason !== "stopped") {
|
|
647
752
|
await player.queue.utils.save();
|
|
@@ -661,31 +766,5 @@ class LavalinkNode {
|
|
|
661
766
|
}
|
|
662
767
|
return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
|
|
663
768
|
}
|
|
664
|
-
async trackStuck(player, track, payload) {
|
|
665
|
-
this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
|
|
666
|
-
// If there are no songs in the queue
|
|
667
|
-
if (!player.queue.tracks.length && player.repeatMode === "off")
|
|
668
|
-
return;
|
|
669
|
-
// remove the current track, and enqueue the next one
|
|
670
|
-
await (0, Utils_1.queueTrackEnd)(player);
|
|
671
|
-
// if no track available, end queue
|
|
672
|
-
if (!player.queue.current)
|
|
673
|
-
return this.queueEnd(player, track, payload);
|
|
674
|
-
// play track if autoSkip is true
|
|
675
|
-
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
676
|
-
}
|
|
677
|
-
async trackError(player, track, payload) {
|
|
678
|
-
this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
|
|
679
|
-
// remove the current track, and enqueue the next one
|
|
680
|
-
await (0, Utils_1.queueTrackEnd)(player);
|
|
681
|
-
// if no track available, end queue
|
|
682
|
-
if (!player.queue.current)
|
|
683
|
-
return this.queueEnd(player, track, payload);
|
|
684
|
-
// play track if autoSkip is true
|
|
685
|
-
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
686
|
-
}
|
|
687
|
-
socketClosed(player, payload) {
|
|
688
|
-
return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
|
|
689
|
-
}
|
|
690
769
|
}
|
|
691
770
|
exports.LavalinkNode = LavalinkNode;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EQBand, FilterData, FilterManager, LavalinkFilterData } from "./Filters";
|
|
2
2
|
import { LavalinkManager } from "./LavalinkManager";
|
|
3
|
-
import { LavalinkNode } from "./Node";
|
|
3
|
+
import { LavalinkNode, SponsorBlockSegment } from "./Node";
|
|
4
4
|
import { Queue } from "./Queue";
|
|
5
5
|
import { Track, UnresolvedTrack } from "./Track";
|
|
6
6
|
import { LavalinkPlayerVoiceOptions, LavaSearchQuery, SearchQuery } from "./Utils";
|
|
@@ -149,6 +149,9 @@ export declare class Player {
|
|
|
149
149
|
*/
|
|
150
150
|
setVolume(volume: number, ignoreVolumeDecrementer?: boolean): Promise<this>;
|
|
151
151
|
lavaSearch(query: LavaSearchQuery, requestUser: unknown): Promise<import("./Utils").SearchResult | import("./Utils").LavaSearchResponse>;
|
|
152
|
+
setSponsorBlock(segments?: SponsorBlockSegment[]): Promise<void>;
|
|
153
|
+
getSponsorBlock(): Promise<SponsorBlockSegment[]>;
|
|
154
|
+
deleteSponsorBlock(): Promise<void>;
|
|
152
155
|
/**
|
|
153
156
|
*
|
|
154
157
|
* @param query Query for your data
|
|
@@ -177,12 +180,22 @@ export declare class Player {
|
|
|
177
180
|
* Skip the current song, or a specific amount of songs
|
|
178
181
|
* @param amount provide the index of the next track to skip to
|
|
179
182
|
*/
|
|
180
|
-
skip(skipTo?: number): Promise<any>;
|
|
183
|
+
skip(skipTo?: number, throwError?: boolean): Promise<any>;
|
|
184
|
+
/**
|
|
185
|
+
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
186
|
+
* @returns
|
|
187
|
+
*/
|
|
188
|
+
stopPlaying(clearQueue?: boolean, executeAutoplay?: boolean): Promise<this>;
|
|
181
189
|
/**
|
|
182
190
|
* Connects the Player to the Voice Channel
|
|
183
191
|
* @returns
|
|
184
192
|
*/
|
|
185
193
|
connect(): Promise<this>;
|
|
194
|
+
changeVoiceState(data: {
|
|
195
|
+
voiceChannelId?: string;
|
|
196
|
+
selfDeaf?: boolean;
|
|
197
|
+
selfMute?: boolean;
|
|
198
|
+
}): Promise<this>;
|
|
186
199
|
/**
|
|
187
200
|
* Disconnects the Player from the Voice Channel, but keeps the player in the cache
|
|
188
201
|
* @param force If false it throws an error, if player thinks it's already disconnected
|