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
|
@@ -215,6 +215,15 @@ class Player {
|
|
|
215
215
|
async lavaSearch(query, requestUser) {
|
|
216
216
|
return this.node.lavaSearch(query, requestUser);
|
|
217
217
|
}
|
|
218
|
+
async setSponsorBlock(segments = ["sponsor", "selfpromo"]) {
|
|
219
|
+
return this.node.setSponsorBlock(this, segments);
|
|
220
|
+
}
|
|
221
|
+
async getSponsorBlock() {
|
|
222
|
+
return this.node.getSponsorBlock(this);
|
|
223
|
+
}
|
|
224
|
+
async deleteSponsorBlock() {
|
|
225
|
+
return this.node.deleteSponsorBlock(this);
|
|
226
|
+
}
|
|
218
227
|
/**
|
|
219
228
|
*
|
|
220
229
|
* @param query Query for your data
|
|
@@ -222,10 +231,6 @@ class Player {
|
|
|
222
231
|
*/
|
|
223
232
|
async search(query, requestUser) {
|
|
224
233
|
const Query = this.LavalinkManager.utils.transformQuery(query);
|
|
225
|
-
if (/^https?:\/\//.test(Query.query))
|
|
226
|
-
this.LavalinkManager.utils.validateQueryString(this.node, Query.source);
|
|
227
|
-
else if (Query.source)
|
|
228
|
-
this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
|
|
229
234
|
if (["bcsearch", "bandcamp"].includes(Query.source))
|
|
230
235
|
return await (0, BandCampSearch_1.bandCampSearch)(this, Query.query, requestUser);
|
|
231
236
|
return this.node.search(Query, requestUser);
|
|
@@ -289,8 +294,8 @@ class Player {
|
|
|
289
294
|
* Skip the current song, or a specific amount of songs
|
|
290
295
|
* @param amount provide the index of the next track to skip to
|
|
291
296
|
*/
|
|
292
|
-
async skip(skipTo = 0) {
|
|
293
|
-
if (!this.queue.tracks.length)
|
|
297
|
+
async skip(skipTo = 0, throwError = true) {
|
|
298
|
+
if (!this.queue.tracks.length && (throwError || (typeof skipTo === "boolean" && skipTo === true)))
|
|
294
299
|
throw new RangeError("Can't skip more than the queue size");
|
|
295
300
|
if (typeof skipTo === "number" && skipTo > 1) {
|
|
296
301
|
if (skipTo > this.queue.tracks.length)
|
|
@@ -304,13 +309,33 @@ class Player {
|
|
|
304
309
|
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
305
310
|
return this;
|
|
306
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
314
|
+
* @returns
|
|
315
|
+
*/
|
|
316
|
+
async stopPlaying(clearQueue = true, executeAutoplay = false) {
|
|
317
|
+
// use internal_stopPlaying on true, so that it doesn't utilize current loop states. on trackEnd event
|
|
318
|
+
this.set("internal_stopPlaying", true);
|
|
319
|
+
// remove tracks from the queue
|
|
320
|
+
if (this.queue.tracks.length && clearQueue === true)
|
|
321
|
+
await this.queue.splice(0, this.queue.tracks.length);
|
|
322
|
+
if (executeAutoplay === false)
|
|
323
|
+
this.set("internal_autoplayStopPlaying", true);
|
|
324
|
+
else
|
|
325
|
+
this.set("internal_autoplayStopPlaying", undefined);
|
|
326
|
+
const now = performance.now();
|
|
327
|
+
// send to lavalink, that it should stop playing
|
|
328
|
+
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { encodedTrack: null } });
|
|
329
|
+
this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
307
332
|
/**
|
|
308
333
|
* Connects the Player to the Voice Channel
|
|
309
334
|
* @returns
|
|
310
335
|
*/
|
|
311
336
|
async connect() {
|
|
312
337
|
if (!this.options.voiceChannelId)
|
|
313
|
-
throw new RangeError("No Voice Channel id has been set.");
|
|
338
|
+
throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
314
339
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
315
340
|
op: 4,
|
|
316
341
|
d: {
|
|
@@ -320,6 +345,26 @@ class Player {
|
|
|
320
345
|
self_deaf: this.options.selfDeaf ?? true,
|
|
321
346
|
}
|
|
322
347
|
});
|
|
348
|
+
this.voiceChannelId = this.options.voiceChannelId;
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
351
|
+
async changeVoiceState(data) {
|
|
352
|
+
if (this.options.voiceChannelId === data.voiceChannelId)
|
|
353
|
+
throw new RangeError("New Channel can't be equal to the old Channel.");
|
|
354
|
+
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
355
|
+
op: 4,
|
|
356
|
+
d: {
|
|
357
|
+
guild_id: this.guildId,
|
|
358
|
+
channel_id: data.voiceChannelId,
|
|
359
|
+
self_mute: data.selfMute ?? this.options.selfMute ?? false,
|
|
360
|
+
self_deaf: data.selfDeaf ?? this.options.selfDeaf ?? true,
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
// override the options
|
|
364
|
+
this.options.voiceChannelId = data.voiceChannelId;
|
|
365
|
+
this.options.selfMute = data.selfMute;
|
|
366
|
+
this.options.selfDeaf = data.selfDeaf;
|
|
367
|
+
this.voiceChannelId = data.voiceChannelId;
|
|
323
368
|
return this;
|
|
324
369
|
}
|
|
325
370
|
/**
|
|
@@ -329,7 +374,7 @@ class Player {
|
|
|
329
374
|
*/
|
|
330
375
|
async disconnect(force = false) {
|
|
331
376
|
if (!force && !this.options.voiceChannelId)
|
|
332
|
-
throw new RangeError("No Voice Channel id has been set.");
|
|
377
|
+
throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
333
378
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
334
379
|
op: 4,
|
|
335
380
|
d: {
|
|
@@ -346,10 +391,10 @@ class Player {
|
|
|
346
391
|
* Destroy the player and disconnect from the voice channel
|
|
347
392
|
*/
|
|
348
393
|
async destroy(reason, disconnect = true) {
|
|
349
|
-
if (this.LavalinkManager.options.debugOptions.playerDestroy.debugLog)
|
|
394
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
350
395
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Destroy-Reason: ${String(reason)}`);
|
|
351
396
|
if (this.get("internal_destroystatus") === true) {
|
|
352
|
-
if (this.LavalinkManager.options.debugOptions.playerDestroy.debugLog)
|
|
397
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
353
398
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Already destroying somewhere else..`);
|
|
354
399
|
return;
|
|
355
400
|
}
|
|
@@ -365,7 +410,7 @@ class Player {
|
|
|
365
410
|
this.LavalinkManager.deletePlayer(this.guildId);
|
|
366
411
|
// destroy the player on lavalink side
|
|
367
412
|
await this.node.destroyPlayer(this.guildId);
|
|
368
|
-
if (this.LavalinkManager.options.debugOptions.playerDestroy.debugLog)
|
|
413
|
+
if (this.LavalinkManager.options.advancedOptions?.debugOptions.playerDestroy.debugLog)
|
|
369
414
|
console.log(`Lavalink-Client-Debug | PlayerDestroy [::] destroy Function, [guildId ${this.guildId}] - Player got destroyed successfully`);
|
|
370
415
|
// emit the event
|
|
371
416
|
this.LavalinkManager.emit("playerDestroy", this, reason);
|
|
@@ -18,7 +18,7 @@ export interface QueueStoreManager extends Record<string, any> {
|
|
|
18
18
|
parse: (value: unknown) => Promise<Partial<StoredQueue>>;
|
|
19
19
|
}
|
|
20
20
|
export interface ManagerQueueOptions {
|
|
21
|
-
/** Maximum Amount of tracks for the queue.previous array */
|
|
21
|
+
/** Maximum Amount of tracks for the queue.previous array. Set to 0 to not save previous songs. Defaults to 25 Tracks */
|
|
22
22
|
maxPreviousTracks?: number;
|
|
23
23
|
/** Custom Queue Store option */
|
|
24
24
|
queueStore?: QueueStoreManager;
|
|
@@ -51,6 +51,17 @@ export interface UnresolvedSearchResult {
|
|
|
51
51
|
playlist: PlaylistInfo | null;
|
|
52
52
|
tracks: UnresolvedTrack[];
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Parses Node Connection Url: "lavalink://<nodeId>:<nodeAuthorization(Password)>@<NodeHost>:<NodePort>"
|
|
56
|
+
* @param connectionUrl
|
|
57
|
+
* @returns
|
|
58
|
+
*/
|
|
59
|
+
export declare function parseLavalinkConnUrl(connectionUrl: string): {
|
|
60
|
+
authorization: string;
|
|
61
|
+
id: string;
|
|
62
|
+
host: string;
|
|
63
|
+
port: number;
|
|
64
|
+
};
|
|
54
65
|
export declare class ManagerUtils {
|
|
55
66
|
LavalinkManager: LavalinkManager | null;
|
|
56
67
|
constructor(LavalinkManager?: LavalinkManager);
|
|
@@ -152,7 +163,7 @@ export declare class MiniMap<K, V> extends Map<K, V> {
|
|
|
152
163
|
map<T>(fn: (value: V, key: K, miniMap: this) => T): T[];
|
|
153
164
|
map<This, T>(fn: (this: This, value: V, key: K, miniMap: this) => T, thisArg: This): T[];
|
|
154
165
|
}
|
|
155
|
-
export type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent;
|
|
166
|
+
export type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents;
|
|
156
167
|
export type Severity = "COMMON" | "SUSPICIOUS" | "FAULT";
|
|
157
168
|
export interface Exception {
|
|
158
169
|
severity: Severity;
|
|
@@ -188,9 +199,52 @@ export interface WebSocketClosedEvent extends PlayerEvent {
|
|
|
188
199
|
byRemote: boolean;
|
|
189
200
|
reason: string;
|
|
190
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Types & Events for Sponsorblock-plugin from Lavalink: https://github.com/topi314/Sponsorblock-Plugin#segmentsloaded
|
|
204
|
+
*/
|
|
205
|
+
export type SponsorBlockSegmentEvents = SponsorBlockSegmentSkipped | SponsorBlockSegmentsLoaded | SponsorBlockChapterStarted | SponsorBlockChaptersLoaded;
|
|
206
|
+
export type SponsorBlockSegmentEventType = "SegmentSkipped" | "SegmentsLoaded" | "ChaptersLoaded" | "ChapterStarted";
|
|
207
|
+
export interface SponsorBlockSegmentsLoaded extends PlayerEvent {
|
|
208
|
+
type: "SegmentsLoaded";
|
|
209
|
+
segments: {
|
|
210
|
+
category: string;
|
|
211
|
+
start: number;
|
|
212
|
+
end: number;
|
|
213
|
+
}[];
|
|
214
|
+
}
|
|
215
|
+
export interface SponsorBlockSegmentSkipped extends PlayerEvent {
|
|
216
|
+
type: "SegmentSkipped";
|
|
217
|
+
segment: {
|
|
218
|
+
category: string;
|
|
219
|
+
start: number;
|
|
220
|
+
end: number;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
export interface SponsorBlockChapterStarted extends PlayerEvent {
|
|
224
|
+
type: "ChapterStarted";
|
|
225
|
+
/** The Chapter which started */
|
|
226
|
+
chapter: {
|
|
227
|
+
/** The Name of the Chapter */
|
|
228
|
+
name: string;
|
|
229
|
+
start: number;
|
|
230
|
+
end: number;
|
|
231
|
+
duration: number;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
export interface SponsorBlockChaptersLoaded extends PlayerEvent {
|
|
235
|
+
type: "ChaptersLoaded";
|
|
236
|
+
/** All Chapters loaded */
|
|
237
|
+
chapters: {
|
|
238
|
+
/** The Name of the Chapter */
|
|
239
|
+
name: string;
|
|
240
|
+
start: number;
|
|
241
|
+
end: number;
|
|
242
|
+
duration: number;
|
|
243
|
+
}[];
|
|
244
|
+
}
|
|
191
245
|
export type LoadTypes = "track" | "playlist" | "search" | "error" | "empty";
|
|
192
246
|
export type State = "CONNECTED" | "CONNECTING" | "DISCONNECTED" | "DISCONNECTING" | "DESTROYING";
|
|
193
|
-
export type PlayerEventType = "TrackStartEvent" | "TrackEndEvent" | "TrackExceptionEvent" | "TrackStuckEvent" | "WebSocketClosedEvent";
|
|
247
|
+
export type PlayerEventType = "TrackStartEvent" | "TrackEndEvent" | "TrackExceptionEvent" | "TrackStuckEvent" | "WebSocketClosedEvent" | SponsorBlockSegmentEventType;
|
|
194
248
|
export type TrackEndReason = "finished" | "loadFailed" | "stopped" | "replaced" | "cleanup";
|
|
195
249
|
export interface InvalidLavalinkRestRequest {
|
|
196
250
|
timestamp: number;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.queueTrackEnd = exports.MiniMap = exports.ManagerUtils = exports.NodeSymbol = exports.QueueSymbol = exports.UnresolvedTrackSymbol = exports.TrackSymbol = void 0;
|
|
3
|
+
exports.queueTrackEnd = exports.MiniMap = exports.ManagerUtils = exports.parseLavalinkConnUrl = exports.NodeSymbol = exports.QueueSymbol = exports.UnresolvedTrackSymbol = exports.TrackSymbol = void 0;
|
|
4
|
+
const node_url_1 = require("node:url");
|
|
5
|
+
const types_1 = require("node:util/types");
|
|
4
6
|
const LavalinkManagerStatics_1 = require("./LavalinkManagerStatics");
|
|
5
7
|
exports.TrackSymbol = Symbol("LC-Track");
|
|
6
8
|
exports.UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
|
|
@@ -8,6 +10,23 @@ exports.QueueSymbol = Symbol("LC-Queue");
|
|
|
8
10
|
exports.NodeSymbol = Symbol("LC-Node");
|
|
9
11
|
/** @hidden */
|
|
10
12
|
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
/**
|
|
14
|
+
* Parses Node Connection Url: "lavalink://<nodeId>:<nodeAuthorization(Password)>@<NodeHost>:<NodePort>"
|
|
15
|
+
* @param connectionUrl
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
function parseLavalinkConnUrl(connectionUrl) {
|
|
19
|
+
if (!connectionUrl.startsWith("lavalink://"))
|
|
20
|
+
throw new Error(`ConnectionUrl (${connectionUrl}) must start with 'lavalink://'`);
|
|
21
|
+
const parsed = new node_url_1.URL(connectionUrl);
|
|
22
|
+
return {
|
|
23
|
+
authorization: parsed.password,
|
|
24
|
+
id: parsed.username,
|
|
25
|
+
host: parsed.hostname,
|
|
26
|
+
port: Number(parsed.port),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
exports.parseLavalinkConnUrl = parseLavalinkConnUrl;
|
|
11
30
|
class ManagerUtils {
|
|
12
31
|
LavalinkManager = null;
|
|
13
32
|
constructor(LavalinkManager) {
|
|
@@ -166,6 +185,18 @@ class ManagerUtils {
|
|
|
166
185
|
throw new Error("No Lavalink Node was provided");
|
|
167
186
|
if (!node.info.sourceManagers?.length)
|
|
168
187
|
throw new Error("Lavalink Node, has no sourceManagers enabled");
|
|
188
|
+
// checks for blacklisted links / domains / queries
|
|
189
|
+
if (this.LavalinkManager.options?.linksBlacklist?.length > 0 && this.LavalinkManager.options?.linksBlacklist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || (0, types_1.isRegExp)(v) && v.test(queryString))) {
|
|
190
|
+
throw new Error(`Query string contains a link / word which is blacklisted.`);
|
|
191
|
+
}
|
|
192
|
+
if (!/^https?:\/\//.test(queryString))
|
|
193
|
+
return;
|
|
194
|
+
else if (this.LavalinkManager.options?.linksAllowed === false)
|
|
195
|
+
throw new Error("Using links to make a request is not allowed.");
|
|
196
|
+
// checks for if the query is whitelisted (should only work for links, so it skips the check for no link queries)
|
|
197
|
+
if (this.LavalinkManager.options?.linksWhitelist?.length > 0 && !this.LavalinkManager.options?.linksWhitelist.some(v => (typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase()))) || (0, types_1.isRegExp)(v) && v.test(queryString))) {
|
|
198
|
+
throw new Error(`Query string contains a link / word which isn't whitelisted.`);
|
|
199
|
+
}
|
|
169
200
|
// missing links: beam.pro local getyarn.io clypit pornhub reddit ocreamix soundgasm
|
|
170
201
|
if ((LavalinkManagerStatics_1.SourceLinksRegexes.YoutubeMusicRegex.test(queryString) || LavalinkManagerStatics_1.SourceLinksRegexes.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
|
|
171
202
|
throw new Error("Lavalink Node has not 'youtube' enabled");
|
|
@@ -2,6 +2,7 @@ import { fetch } from "undici";
|
|
|
2
2
|
export const bandCampSearch = async (player, query, requestUser) => {
|
|
3
3
|
let error = null;
|
|
4
4
|
let tracks = [];
|
|
5
|
+
player.LavalinkManager.utils.validateQueryString(player.node, query);
|
|
5
6
|
try {
|
|
6
7
|
const data = await fetch(`https://bandcamp.com/api/nusearch/2/autocomplete?q=${encodeURIComponent(query)}`, {
|
|
7
8
|
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;
|
|
@@ -34,17 +34,22 @@ export class LavalinkManager extends EventEmitter {
|
|
|
34
34
|
requesterTransformer: options?.playerOptions?.requesterTransformer ?? null,
|
|
35
35
|
useUnresolvedData: options?.playerOptions?.useUnresolvedData ?? false,
|
|
36
36
|
},
|
|
37
|
+
linksWhitelist: options?.linksWhitelist ?? [],
|
|
38
|
+
linksBlacklist: options?.linksBlacklist ?? [],
|
|
37
39
|
autoSkip: options?.autoSkip ?? true,
|
|
40
|
+
emitNewSongsOnly: options?.emitNewSongsOnly ?? false,
|
|
38
41
|
queueOptions: {
|
|
39
42
|
maxPreviousTracks: options?.queueOptions?.maxPreviousTracks ?? 25,
|
|
40
43
|
queueChangesWatcher: options?.queueOptions?.queueChangesWatcher ?? null,
|
|
41
44
|
queueStore: options?.queueOptions?.queueStore ?? new DefaultQueueStore(),
|
|
42
45
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
advancedOptions: {
|
|
47
|
+
debugOptions: {
|
|
48
|
+
noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
|
|
49
|
+
playerDestroy: {
|
|
50
|
+
dontThrowError: options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError ?? false,
|
|
51
|
+
debugLog: options?.advancedOptions?.debugOptions?.playerDestroy?.debugLog ?? false,
|
|
52
|
+
}
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
};
|
|
@@ -57,6 +62,8 @@ export class LavalinkManager extends EventEmitter {
|
|
|
57
62
|
// 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");
|
|
58
63
|
if (options?.autoSkip && typeof options?.autoSkip !== "boolean")
|
|
59
64
|
throw new SyntaxError("ManagerOption.autoSkip must be either false | true aka boolean");
|
|
65
|
+
if (options?.emitNewSongsOnly && typeof options?.emitNewSongsOnly !== "boolean")
|
|
66
|
+
throw new SyntaxError("ManagerOption.emitNewSongsOnly must be either false | true aka boolean");
|
|
60
67
|
if (!options?.nodes || !Array.isArray(options?.nodes) || !options?.nodes.every(node => this.utils.isNodeOptions(node)))
|
|
61
68
|
throw new SyntaxError("ManagerOption.nodes must be an Array of NodeOptions and is required of at least 1 Node");
|
|
62
69
|
/* QUEUE STORE */
|
|
@@ -110,7 +117,7 @@ export class LavalinkManager extends EventEmitter {
|
|
|
110
117
|
return;
|
|
111
118
|
// oldPlayer.connected is operational. you could also do oldPlayer.voice?.token
|
|
112
119
|
if (oldPlayer.voiceChannelId === "string" && oldPlayer.connected && !oldPlayer.get("internal_destroywithoutdisconnect")) {
|
|
113
|
-
if (!this.options?.debugOptions?.playerDestroy?.dontThrowError)
|
|
120
|
+
if (!this.options?.advancedOptions?.debugOptions?.playerDestroy?.dontThrowError)
|
|
114
121
|
throw new Error(`Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player ${JSON.stringify(oldPlayer.toJSON?.())}`);
|
|
115
122
|
else
|
|
116
123
|
console.error("Use Player#destroy() not LavalinkManager#deletePlayer() to stop the Player", oldPlayer.toJSON?.());
|
|
@@ -156,12 +163,12 @@ export class LavalinkManager extends EventEmitter {
|
|
|
156
163
|
*/
|
|
157
164
|
async sendRawData(data) {
|
|
158
165
|
if (!this.initiated) {
|
|
159
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
166
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
160
167
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, manager is not initated yet");
|
|
161
168
|
return;
|
|
162
169
|
}
|
|
163
170
|
if (!("t" in data)) {
|
|
164
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
171
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
165
172
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 't' in payload-data of the raw event:", data);
|
|
166
173
|
return;
|
|
167
174
|
}
|
|
@@ -178,23 +185,23 @@ export class LavalinkManager extends EventEmitter {
|
|
|
178
185
|
if (["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(data.t)) {
|
|
179
186
|
const update = ("d" in data ? data.d : data);
|
|
180
187
|
if (!update) {
|
|
181
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
188
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
182
189
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no update data found in payload:", data);
|
|
183
190
|
return;
|
|
184
191
|
}
|
|
185
192
|
if (!("token" in update) && !("session_id" in update)) {
|
|
186
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
193
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
187
194
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, no 'token' nor 'session_id' found in payload:", data);
|
|
188
195
|
return;
|
|
189
196
|
}
|
|
190
197
|
const player = this.getPlayer(update.guild_id);
|
|
191
198
|
if (!player) {
|
|
192
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
199
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
193
200
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, No Lavalink Player found via key: 'guild_id' of update-data:", update);
|
|
194
201
|
return;
|
|
195
202
|
}
|
|
196
203
|
if (player.get("internal_destroystatus") === true) {
|
|
197
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
204
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
198
205
|
console.debug("Lavalink-Client-Debug | NO-AUDIO [::] sendRawData function, Player is in a destroying state. can't signal the voice states");
|
|
199
206
|
return;
|
|
200
207
|
}
|
|
@@ -211,13 +218,13 @@ export class LavalinkManager extends EventEmitter {
|
|
|
211
218
|
}
|
|
212
219
|
}
|
|
213
220
|
});
|
|
214
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
221
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
215
222
|
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, } });
|
|
216
223
|
return;
|
|
217
224
|
}
|
|
218
225
|
/* voice state update */
|
|
219
226
|
if (update.user_id !== this.options?.client.id) {
|
|
220
|
-
if (this.options?.debugOptions?.noAudio === true)
|
|
227
|
+
if (this.options?.advancedOptions?.debugOptions?.noAudio === true)
|
|
221
228
|
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);
|
|
222
229
|
return;
|
|
223
230
|
}
|
|
@@ -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
|
}
|