lavalink-client 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/cjs/structures/Node.d.ts +5 -5
- package/dist/cjs/structures/Node.js +56 -47
- package/dist/cjs/structures/Player.d.ts +1 -1
- package/dist/cjs/structures/Player.js +8 -2
- package/dist/cjs/structures/Utils.d.ts +11 -0
- package/dist/cjs/structures/Utils.js +20 -2
- package/dist/esm/structures/Node.d.ts +5 -5
- package/dist/esm/structures/Node.js +56 -47
- package/dist/esm/structures/Player.d.ts +1 -1
- package/dist/esm/structures/Player.js +8 -2
- package/dist/esm/structures/Utils.d.ts +11 -0
- package/dist/esm/structures/Utils.js +18 -1
- package/dist/types/structures/Node.d.ts +5 -5
- package/dist/types/structures/Player.d.ts +1 -1
- package/dist/types/structures/Utils.d.ts +11 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,9 +111,22 @@ Check out the [Documentation](https://lc4.gitbook.io/lavalink-client) for **Exam
|
|
|
111
111
|
- `player.deleteSponsorBlock()` / `node.deleteSponsorBlock()`
|
|
112
112
|
- That Plugin adds following **Events** to the **Manager**: `"SegmentsLoaded"`, `"SegmentSkipped"`, `"ChapterStarted"`, `"ChaptersLoaded"`
|
|
113
113
|
- Example Bot show example in autoplayFunction how to "disable" / "enable" Autoplay with bot data variables.
|
|
114
|
-
- Added `ManagerOptions#
|
|
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
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
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
117
|
- Added `ManagerOptions#linksAllowed` if set to false, it does not allow requests which are links
|
|
118
118
|
- Moved `ManaagerOptions#debugOptions` to `ManaagerOptions#advancedOptions.debugOptions`
|
|
119
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
|
|
@@ -240,17 +240,17 @@ export declare class LavalinkNode {
|
|
|
240
240
|
private error;
|
|
241
241
|
private message;
|
|
242
242
|
private handleEvent;
|
|
243
|
+
private trackStart;
|
|
244
|
+
private trackEnd;
|
|
245
|
+
private trackStuck;
|
|
246
|
+
private trackError;
|
|
247
|
+
private socketClosed;
|
|
243
248
|
private SponsorBlockSegmentLoaded;
|
|
244
249
|
private SponsorBlockSegmentkipped;
|
|
245
250
|
private SponsorBlockChaptersLoaded;
|
|
246
251
|
private SponsorBlockChapterStarted;
|
|
247
|
-
private trackStart;
|
|
248
|
-
private trackEnd;
|
|
249
252
|
getSponsorBlock(player: Player): Promise<SponsorBlockSegment[]>;
|
|
250
253
|
setSponsorBlock(player: Player, segments?: SponsorBlockSegment[]): Promise<void>;
|
|
251
254
|
deleteSponsorBlock(player: Player): Promise<void>;
|
|
252
255
|
private queueEnd;
|
|
253
|
-
private trackStuck;
|
|
254
|
-
private trackError;
|
|
255
|
-
private socketClosed;
|
|
256
256
|
}
|
|
@@ -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;
|
|
@@ -606,18 +607,7 @@ class LavalinkNode {
|
|
|
606
607
|
}
|
|
607
608
|
return;
|
|
608
609
|
}
|
|
609
|
-
|
|
610
|
-
return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track, payload);
|
|
611
|
-
}
|
|
612
|
-
SponsorBlockSegmentkipped(player, track, payload) {
|
|
613
|
-
return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track, payload);
|
|
614
|
-
}
|
|
615
|
-
SponsorBlockChaptersLoaded(player, track, payload) {
|
|
616
|
-
return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track, payload);
|
|
617
|
-
}
|
|
618
|
-
SponsorBlockChapterStarted(player, track, payload) {
|
|
619
|
-
return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track, payload);
|
|
620
|
-
}
|
|
610
|
+
// LAVALINK EVENT HANDLING FUNCTIONS
|
|
621
611
|
trackStart(player, track, payload) {
|
|
622
612
|
player.playing = true;
|
|
623
613
|
player.paused = false;
|
|
@@ -628,7 +618,7 @@ class LavalinkNode {
|
|
|
628
618
|
}
|
|
629
619
|
async trackEnd(player, track, payload) {
|
|
630
620
|
// If there are no songs in the queue
|
|
631
|
-
if (!player.queue.tracks.length && player.repeatMode === "off")
|
|
621
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
632
622
|
return this.queueEnd(player, track, payload);
|
|
633
623
|
// If a track was forcibly played
|
|
634
624
|
if (payload.reason === "replaced")
|
|
@@ -661,6 +651,49 @@ class LavalinkNode {
|
|
|
661
651
|
// play track if autoSkip is true
|
|
662
652
|
return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
|
|
663
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
|
|
664
697
|
async getSponsorBlock(player) {
|
|
665
698
|
// no plugin enabled
|
|
666
699
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
@@ -679,10 +712,10 @@ class LavalinkNode {
|
|
|
679
712
|
if (segments.some(v => !exports.validSponsorBlocks.includes(v.toLowerCase())))
|
|
680
713
|
throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${exports.validSponsorBlocks.map(v => `'${v}'`).join(", ")}`);
|
|
681
714
|
// do the request
|
|
682
|
-
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
715
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
716
|
+
r.method = "PUT";
|
|
717
|
+
r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
|
|
718
|
+
r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
|
|
686
719
|
});
|
|
687
720
|
return;
|
|
688
721
|
}
|
|
@@ -691,17 +724,18 @@ class LavalinkNode {
|
|
|
691
724
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
692
725
|
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
693
726
|
// do the request
|
|
694
|
-
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (
|
|
695
|
-
|
|
696
|
-
return request;
|
|
727
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
728
|
+
r.method = "DELETE";
|
|
697
729
|
});
|
|
698
730
|
return;
|
|
699
731
|
}
|
|
732
|
+
// UTIL FOR QUEUE END
|
|
700
733
|
async queueEnd(player, track, payload) {
|
|
701
734
|
// add previous track to the queue!
|
|
702
735
|
player.queue.current = null;
|
|
703
736
|
player.playing = false;
|
|
704
|
-
|
|
737
|
+
player.set("internal_stopPlaying", undefined);
|
|
738
|
+
if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function" && typeof player.get("internal_autoplayStopPlaying") === "undefined") {
|
|
705
739
|
await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
706
740
|
if (player.queue.tracks.length > 0)
|
|
707
741
|
await (0, Utils_1.queueTrackEnd)(player);
|
|
@@ -711,6 +745,7 @@ class LavalinkNode {
|
|
|
711
745
|
return player.play({ noReplace: true, paused: false });
|
|
712
746
|
}
|
|
713
747
|
}
|
|
748
|
+
player.set("internal_autoplayStopPlaying", undefined);
|
|
714
749
|
player.queue.previous.unshift(track);
|
|
715
750
|
if (payload?.reason !== "stopped") {
|
|
716
751
|
await player.queue.utils.save();
|
|
@@ -730,31 +765,5 @@ class LavalinkNode {
|
|
|
730
765
|
}
|
|
731
766
|
return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
|
|
732
767
|
}
|
|
733
|
-
async trackStuck(player, track, payload) {
|
|
734
|
-
this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
|
|
735
|
-
// If there are no songs in the queue
|
|
736
|
-
if (!player.queue.tracks.length && player.repeatMode === "off")
|
|
737
|
-
return;
|
|
738
|
-
// remove the current track, and enqueue the next one
|
|
739
|
-
await (0, Utils_1.queueTrackEnd)(player);
|
|
740
|
-
// if no track available, end queue
|
|
741
|
-
if (!player.queue.current)
|
|
742
|
-
return this.queueEnd(player, track, payload);
|
|
743
|
-
// play track if autoSkip is true
|
|
744
|
-
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
745
|
-
}
|
|
746
|
-
async trackError(player, track, payload) {
|
|
747
|
-
this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
|
|
748
|
-
// remove the current track, and enqueue the next one
|
|
749
|
-
await (0, Utils_1.queueTrackEnd)(player);
|
|
750
|
-
// if no track available, end queue
|
|
751
|
-
if (!player.queue.current)
|
|
752
|
-
return this.queueEnd(player, track, payload);
|
|
753
|
-
// play track if autoSkip is true
|
|
754
|
-
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
755
|
-
}
|
|
756
|
-
socketClosed(player, payload) {
|
|
757
|
-
return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
|
|
758
|
-
}
|
|
759
768
|
}
|
|
760
769
|
exports.LavalinkNode = LavalinkNode;
|
|
@@ -185,7 +185,7 @@ export declare class Player {
|
|
|
185
185
|
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
186
186
|
* @returns
|
|
187
187
|
*/
|
|
188
|
-
stopPlaying(): Promise<this>;
|
|
188
|
+
stopPlaying(clearQueue?: boolean, executeAutoplay?: boolean): Promise<this>;
|
|
189
189
|
/**
|
|
190
190
|
* Connects the Player to the Voice Channel
|
|
191
191
|
* @returns
|
|
@@ -313,10 +313,16 @@ class Player {
|
|
|
313
313
|
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
314
314
|
* @returns
|
|
315
315
|
*/
|
|
316
|
-
async stopPlaying() {
|
|
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);
|
|
317
319
|
// remove tracks from the queue
|
|
318
|
-
if (this.queue.tracks.length)
|
|
320
|
+
if (this.queue.tracks.length && clearQueue === true)
|
|
319
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);
|
|
320
326
|
const now = performance.now();
|
|
321
327
|
// send to lavalink, that it should stop playing
|
|
322
328
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { encodedTrack: null } });
|
|
@@ -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);
|
|
@@ -1,7 +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;
|
|
4
|
-
const
|
|
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");
|
|
5
6
|
const LavalinkManagerStatics_1 = require("./LavalinkManagerStatics");
|
|
6
7
|
exports.TrackSymbol = Symbol("LC-Track");
|
|
7
8
|
exports.UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
|
|
@@ -9,6 +10,23 @@ exports.QueueSymbol = Symbol("LC-Queue");
|
|
|
9
10
|
exports.NodeSymbol = Symbol("LC-Node");
|
|
10
11
|
/** @hidden */
|
|
11
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;
|
|
12
30
|
class ManagerUtils {
|
|
13
31
|
LavalinkManager = null;
|
|
14
32
|
constructor(LavalinkManager) {
|
|
@@ -240,17 +240,17 @@ export declare class LavalinkNode {
|
|
|
240
240
|
private error;
|
|
241
241
|
private message;
|
|
242
242
|
private handleEvent;
|
|
243
|
+
private trackStart;
|
|
244
|
+
private trackEnd;
|
|
245
|
+
private trackStuck;
|
|
246
|
+
private trackError;
|
|
247
|
+
private socketClosed;
|
|
243
248
|
private SponsorBlockSegmentLoaded;
|
|
244
249
|
private SponsorBlockSegmentkipped;
|
|
245
250
|
private SponsorBlockChaptersLoaded;
|
|
246
251
|
private SponsorBlockChapterStarted;
|
|
247
|
-
private trackStart;
|
|
248
|
-
private trackEnd;
|
|
249
252
|
getSponsorBlock(player: Player): Promise<SponsorBlockSegment[]>;
|
|
250
253
|
setSponsorBlock(player: Player, segments?: SponsorBlockSegment[]): Promise<void>;
|
|
251
254
|
deleteSponsorBlock(player: Player): Promise<void>;
|
|
252
255
|
private queueEnd;
|
|
253
|
-
private trackStuck;
|
|
254
|
-
private trackError;
|
|
255
|
-
private socketClosed;
|
|
256
256
|
}
|
|
@@ -562,6 +562,7 @@ export class LavalinkNode {
|
|
|
562
562
|
return;
|
|
563
563
|
}
|
|
564
564
|
}
|
|
565
|
+
// LAVALINK EVENT HANDLING UTIL FUNCTION
|
|
565
566
|
async handleEvent(payload) {
|
|
566
567
|
if (!payload.guildId)
|
|
567
568
|
return;
|
|
@@ -602,18 +603,7 @@ export class LavalinkNode {
|
|
|
602
603
|
}
|
|
603
604
|
return;
|
|
604
605
|
}
|
|
605
|
-
|
|
606
|
-
return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track, payload);
|
|
607
|
-
}
|
|
608
|
-
SponsorBlockSegmentkipped(player, track, payload) {
|
|
609
|
-
return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track, payload);
|
|
610
|
-
}
|
|
611
|
-
SponsorBlockChaptersLoaded(player, track, payload) {
|
|
612
|
-
return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track, payload);
|
|
613
|
-
}
|
|
614
|
-
SponsorBlockChapterStarted(player, track, payload) {
|
|
615
|
-
return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track, payload);
|
|
616
|
-
}
|
|
606
|
+
// LAVALINK EVENT HANDLING FUNCTIONS
|
|
617
607
|
trackStart(player, track, payload) {
|
|
618
608
|
player.playing = true;
|
|
619
609
|
player.paused = false;
|
|
@@ -624,7 +614,7 @@ export class LavalinkNode {
|
|
|
624
614
|
}
|
|
625
615
|
async trackEnd(player, track, payload) {
|
|
626
616
|
// If there are no songs in the queue
|
|
627
|
-
if (!player.queue.tracks.length && player.repeatMode === "off")
|
|
617
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
628
618
|
return this.queueEnd(player, track, payload);
|
|
629
619
|
// If a track was forcibly played
|
|
630
620
|
if (payload.reason === "replaced")
|
|
@@ -657,6 +647,49 @@ export class LavalinkNode {
|
|
|
657
647
|
// play track if autoSkip is true
|
|
658
648
|
return this.NodeManager.LavalinkManager.options.autoSkip && player.play({ noReplace: true });
|
|
659
649
|
}
|
|
650
|
+
async trackStuck(player, track, payload) {
|
|
651
|
+
this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
|
|
652
|
+
// If there are no songs in the queue
|
|
653
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
654
|
+
return this.queueEnd(player, track, payload);
|
|
655
|
+
// remove the current track, and enqueue the next one
|
|
656
|
+
await queueTrackEnd(player);
|
|
657
|
+
// if no track available, end queue
|
|
658
|
+
if (!player.queue.current)
|
|
659
|
+
return this.queueEnd(player, track, payload);
|
|
660
|
+
// play track if autoSkip is true
|
|
661
|
+
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
662
|
+
}
|
|
663
|
+
async trackError(player, track, payload) {
|
|
664
|
+
this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
|
|
665
|
+
// If there are no songs in the queue
|
|
666
|
+
if (!player.queue.tracks.length && (player.repeatMode === "off" || player.get("internal_stopPlaying")))
|
|
667
|
+
return this.queueEnd(player, track, payload);
|
|
668
|
+
// remove the current track, and enqueue the next one
|
|
669
|
+
await queueTrackEnd(player);
|
|
670
|
+
// if no track available, end queue
|
|
671
|
+
if (!player.queue.current)
|
|
672
|
+
return this.queueEnd(player, track, payload);
|
|
673
|
+
// play track if autoSkip is true
|
|
674
|
+
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
675
|
+
}
|
|
676
|
+
socketClosed(player, payload) {
|
|
677
|
+
return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
|
|
678
|
+
}
|
|
679
|
+
// SPONSOR BLOCK EVENT FUNCTIONS
|
|
680
|
+
SponsorBlockSegmentLoaded(player, track, payload) {
|
|
681
|
+
return this.NodeManager.LavalinkManager.emit("SegmentsLoaded", player, track, payload);
|
|
682
|
+
}
|
|
683
|
+
SponsorBlockSegmentkipped(player, track, payload) {
|
|
684
|
+
return this.NodeManager.LavalinkManager.emit("SegmentSkipped", player, track, payload);
|
|
685
|
+
}
|
|
686
|
+
SponsorBlockChaptersLoaded(player, track, payload) {
|
|
687
|
+
return this.NodeManager.LavalinkManager.emit("ChaptersLoaded", player, track, payload);
|
|
688
|
+
}
|
|
689
|
+
SponsorBlockChapterStarted(player, track, payload) {
|
|
690
|
+
return this.NodeManager.LavalinkManager.emit("ChapterStarted", player, track, payload);
|
|
691
|
+
}
|
|
692
|
+
// SPONSOR BLOCK EXECUTE FUNCTIONS
|
|
660
693
|
async getSponsorBlock(player) {
|
|
661
694
|
// no plugin enabled
|
|
662
695
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
@@ -675,10 +708,10 @@ export class LavalinkNode {
|
|
|
675
708
|
if (segments.some(v => !validSponsorBlocks.includes(v.toLowerCase())))
|
|
676
709
|
throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${validSponsorBlocks.map(v => `'${v}'`).join(", ")}`);
|
|
677
710
|
// do the request
|
|
678
|
-
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
711
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
712
|
+
r.method = "PUT";
|
|
713
|
+
r.headers = { Authorization: this.options.authorization, 'Content-Type': 'application/json' };
|
|
714
|
+
r.body = JSON.stringify(segments.map(v => v.toLowerCase()));
|
|
682
715
|
});
|
|
683
716
|
return;
|
|
684
717
|
}
|
|
@@ -687,17 +720,18 @@ export class LavalinkNode {
|
|
|
687
720
|
if (!this.info.plugins.find(v => v.name === "sponsorblock-plugin"))
|
|
688
721
|
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.id}`);
|
|
689
722
|
// do the request
|
|
690
|
-
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (
|
|
691
|
-
|
|
692
|
-
return request;
|
|
723
|
+
await this.request(`/sessions/${this.sessionId}/players/${player.guildId}/sponsorblock/categories`, (r) => {
|
|
724
|
+
r.method = "DELETE";
|
|
693
725
|
});
|
|
694
726
|
return;
|
|
695
727
|
}
|
|
728
|
+
// UTIL FOR QUEUE END
|
|
696
729
|
async queueEnd(player, track, payload) {
|
|
697
730
|
// add previous track to the queue!
|
|
698
731
|
player.queue.current = null;
|
|
699
732
|
player.playing = false;
|
|
700
|
-
|
|
733
|
+
player.set("internal_stopPlaying", undefined);
|
|
734
|
+
if (typeof this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction === "function" && typeof player.get("internal_autoplayStopPlaying") === "undefined") {
|
|
701
735
|
await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
|
|
702
736
|
if (player.queue.tracks.length > 0)
|
|
703
737
|
await queueTrackEnd(player);
|
|
@@ -707,6 +741,7 @@ export class LavalinkNode {
|
|
|
707
741
|
return player.play({ noReplace: true, paused: false });
|
|
708
742
|
}
|
|
709
743
|
}
|
|
744
|
+
player.set("internal_autoplayStopPlaying", undefined);
|
|
710
745
|
player.queue.previous.unshift(track);
|
|
711
746
|
if (payload?.reason !== "stopped") {
|
|
712
747
|
await player.queue.utils.save();
|
|
@@ -726,30 +761,4 @@ export class LavalinkNode {
|
|
|
726
761
|
}
|
|
727
762
|
return this.NodeManager.LavalinkManager.emit("queueEnd", player, track, payload);
|
|
728
763
|
}
|
|
729
|
-
async trackStuck(player, track, payload) {
|
|
730
|
-
this.NodeManager.LavalinkManager.emit("trackStuck", player, track, payload);
|
|
731
|
-
// If there are no songs in the queue
|
|
732
|
-
if (!player.queue.tracks.length && player.repeatMode === "off")
|
|
733
|
-
return;
|
|
734
|
-
// remove the current track, and enqueue the next one
|
|
735
|
-
await queueTrackEnd(player);
|
|
736
|
-
// if no track available, end queue
|
|
737
|
-
if (!player.queue.current)
|
|
738
|
-
return this.queueEnd(player, track, payload);
|
|
739
|
-
// play track if autoSkip is true
|
|
740
|
-
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
741
|
-
}
|
|
742
|
-
async trackError(player, track, payload) {
|
|
743
|
-
this.NodeManager.LavalinkManager.emit("trackError", player, track, payload);
|
|
744
|
-
// remove the current track, and enqueue the next one
|
|
745
|
-
await queueTrackEnd(player);
|
|
746
|
-
// if no track available, end queue
|
|
747
|
-
if (!player.queue.current)
|
|
748
|
-
return this.queueEnd(player, track, payload);
|
|
749
|
-
// play track if autoSkip is true
|
|
750
|
-
return (this.NodeManager.LavalinkManager.options.autoSkip && player.queue.current) && player.play({ noReplace: true });
|
|
751
|
-
}
|
|
752
|
-
socketClosed(player, payload) {
|
|
753
|
-
return this.NodeManager.LavalinkManager.emit("playerSocketClosed", player, payload);
|
|
754
|
-
}
|
|
755
764
|
}
|
|
@@ -185,7 +185,7 @@ export declare class Player {
|
|
|
185
185
|
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
186
186
|
* @returns
|
|
187
187
|
*/
|
|
188
|
-
stopPlaying(): Promise<this>;
|
|
188
|
+
stopPlaying(clearQueue?: boolean, executeAutoplay?: boolean): Promise<this>;
|
|
189
189
|
/**
|
|
190
190
|
* Connects the Player to the Voice Channel
|
|
191
191
|
* @returns
|
|
@@ -310,10 +310,16 @@ export class Player {
|
|
|
310
310
|
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
311
311
|
* @returns
|
|
312
312
|
*/
|
|
313
|
-
async stopPlaying() {
|
|
313
|
+
async stopPlaying(clearQueue = true, executeAutoplay = false) {
|
|
314
|
+
// use internal_stopPlaying on true, so that it doesn't utilize current loop states. on trackEnd event
|
|
315
|
+
this.set("internal_stopPlaying", true);
|
|
314
316
|
// remove tracks from the queue
|
|
315
|
-
if (this.queue.tracks.length)
|
|
317
|
+
if (this.queue.tracks.length && clearQueue === true)
|
|
316
318
|
await this.queue.splice(0, this.queue.tracks.length);
|
|
319
|
+
if (executeAutoplay === false)
|
|
320
|
+
this.set("internal_autoplayStopPlaying", true);
|
|
321
|
+
else
|
|
322
|
+
this.set("internal_autoplayStopPlaying", undefined);
|
|
317
323
|
const now = performance.now();
|
|
318
324
|
// send to lavalink, that it should stop playing
|
|
319
325
|
await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { encodedTrack: null } });
|
|
@@ -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);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { URL } from "node:url";
|
|
2
|
+
import { isRegExp } from "node:util/types";
|
|
2
3
|
import { DefaultSources, LavalinkPlugins, SourceLinksRegexes } from "./LavalinkManagerStatics";
|
|
3
4
|
export const TrackSymbol = Symbol("LC-Track");
|
|
4
5
|
export const UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
|
|
@@ -6,6 +7,22 @@ export const QueueSymbol = Symbol("LC-Queue");
|
|
|
6
7
|
export const NodeSymbol = Symbol("LC-Node");
|
|
7
8
|
/** @hidden */
|
|
8
9
|
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10
|
+
/**
|
|
11
|
+
* Parses Node Connection Url: "lavalink://<nodeId>:<nodeAuthorization(Password)>@<NodeHost>:<NodePort>"
|
|
12
|
+
* @param connectionUrl
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export function parseLavalinkConnUrl(connectionUrl) {
|
|
16
|
+
if (!connectionUrl.startsWith("lavalink://"))
|
|
17
|
+
throw new Error(`ConnectionUrl (${connectionUrl}) must start with 'lavalink://'`);
|
|
18
|
+
const parsed = new URL(connectionUrl);
|
|
19
|
+
return {
|
|
20
|
+
authorization: parsed.password,
|
|
21
|
+
id: parsed.username,
|
|
22
|
+
host: parsed.hostname,
|
|
23
|
+
port: Number(parsed.port),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
9
26
|
export class ManagerUtils {
|
|
10
27
|
LavalinkManager = null;
|
|
11
28
|
constructor(LavalinkManager) {
|
|
@@ -240,17 +240,17 @@ export declare class LavalinkNode {
|
|
|
240
240
|
private error;
|
|
241
241
|
private message;
|
|
242
242
|
private handleEvent;
|
|
243
|
+
private trackStart;
|
|
244
|
+
private trackEnd;
|
|
245
|
+
private trackStuck;
|
|
246
|
+
private trackError;
|
|
247
|
+
private socketClosed;
|
|
243
248
|
private SponsorBlockSegmentLoaded;
|
|
244
249
|
private SponsorBlockSegmentkipped;
|
|
245
250
|
private SponsorBlockChaptersLoaded;
|
|
246
251
|
private SponsorBlockChapterStarted;
|
|
247
|
-
private trackStart;
|
|
248
|
-
private trackEnd;
|
|
249
252
|
getSponsorBlock(player: Player): Promise<SponsorBlockSegment[]>;
|
|
250
253
|
setSponsorBlock(player: Player, segments?: SponsorBlockSegment[]): Promise<void>;
|
|
251
254
|
deleteSponsorBlock(player: Player): Promise<void>;
|
|
252
255
|
private queueEnd;
|
|
253
|
-
private trackStuck;
|
|
254
|
-
private trackError;
|
|
255
|
-
private socketClosed;
|
|
256
256
|
}
|
|
@@ -185,7 +185,7 @@ export declare class Player {
|
|
|
185
185
|
* Clears the queue and stops playing. Does not destroy the Player and not leave the channel
|
|
186
186
|
* @returns
|
|
187
187
|
*/
|
|
188
|
-
stopPlaying(): Promise<this>;
|
|
188
|
+
stopPlaying(clearQueue?: boolean, executeAutoplay?: boolean): Promise<this>;
|
|
189
189
|
/**
|
|
190
190
|
* Connects the Player to the Voice Channel
|
|
191
191
|
* @returns
|
|
@@ -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);
|
package/package.json
CHANGED