@stella_project/stellalib 1.0.2 → 1.1.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 +670 -151
- package/THIRD-PARTY-NOTICES.md +28 -0
- package/dist/Structures/Filters.d.ts +18 -0
- package/dist/Structures/Filters.d.ts.map +1 -1
- package/dist/Structures/Filters.js +24 -0
- package/dist/Structures/Filters.js.map +1 -1
- package/dist/Structures/Manager.d.ts +16 -1
- package/dist/Structures/Manager.d.ts.map +1 -1
- package/dist/Structures/Manager.js +95 -6
- package/dist/Structures/Manager.js.map +1 -1
- package/dist/Structures/Node.d.ts +8 -1
- package/dist/Structures/Node.d.ts.map +1 -1
- package/dist/Structures/Node.js +150 -24
- package/dist/Structures/Node.js.map +1 -1
- package/dist/Structures/Player.d.ts +37 -1
- package/dist/Structures/Player.d.ts.map +1 -1
- package/dist/Structures/Player.js +186 -17
- package/dist/Structures/Player.js.map +1 -1
- package/dist/Structures/Queue.d.ts +9 -0
- package/dist/Structures/Queue.d.ts.map +1 -1
- package/dist/Structures/Queue.js +52 -0
- package/dist/Structures/Queue.js.map +1 -1
- package/dist/Structures/SessionStore.d.ts +8 -3
- package/dist/Structures/SessionStore.d.ts.map +1 -1
- package/dist/Structures/SessionStore.js +33 -3
- package/dist/Structures/SessionStore.js.map +1 -1
- package/dist/Structures/Types.d.ts +84 -0
- package/dist/Structures/Types.d.ts.map +1 -1
- package/dist/Structures/Utils.d.ts +6 -1
- package/dist/Structures/Utils.d.ts.map +1 -1
- package/dist/Structures/Utils.js +14 -0
- package/dist/Structures/Utils.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,44 +1,151 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
A powerful
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">StellaLib</h1>
|
|
3
|
+
<p align="center">A powerful Lavalink v3 + v4 client for TypeScript — with auto version detection, session persistence, smart autoplay, and graceful shutdown.</p>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://www.npmjs.com/package/@stella_project/stellalib"><img src="https://img.shields.io/npm/v/@stella_project/stellalib.svg?style=flat-square&color=blue" alt="npm version" /></a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/@stella_project/stellalib"><img src="https://img.shields.io/npm/dm/@stella_project/stellalib.svg?style=flat-square" alt="npm downloads" /></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-OSL--3.0-blue.svg?style=flat-square" alt="License" /></a>
|
|
10
|
+
<a href="https://github.com/Roki-Stella-Projects/StellaLib"><img src="https://img.shields.io/github/stars/Roki-Stella-Projects/StellaLib?style=flat-square" alt="GitHub stars" /></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [What is StellaLib?](#what-is-stellalib)
|
|
18
|
+
- [How it Works](#how-it-works)
|
|
19
|
+
- [Architecture](#architecture)
|
|
20
|
+
- [Installation](#installation)
|
|
21
|
+
- [Quick Start](#quick-start)
|
|
22
|
+
- [Core Concepts](#core-concepts)
|
|
23
|
+
- [Manager](#manager)
|
|
24
|
+
- [Node](#node)
|
|
25
|
+
- [Player](#player)
|
|
26
|
+
- [Queue](#queue)
|
|
27
|
+
- [Rest](#rest)
|
|
28
|
+
- [Filters](#filters)
|
|
29
|
+
- [Multi-Version Support (v3 + v4)](#multi-version-support-v3--v4)
|
|
30
|
+
- [Session Persistence](#session-persistence)
|
|
31
|
+
- [Player State Persistence](#player-state-persistence)
|
|
32
|
+
- [Auto-Failover](#auto-failover)
|
|
33
|
+
- [Inactivity Timeout](#inactivity-timeout)
|
|
34
|
+
- [Queue Limits & Deduplication](#queue-limits--deduplication)
|
|
35
|
+
- [Node Health Monitoring](#node-health-monitoring)
|
|
36
|
+
- [Smart Autoplay](#smart-autoplay)
|
|
37
|
+
- [Search with Fallback](#search-with-fallback)
|
|
38
|
+
- [Audio Filters](#audio-filters)
|
|
39
|
+
- [Events Reference](#events-reference)
|
|
40
|
+
- [Configuration Reference](#configuration-reference)
|
|
41
|
+
- [Requirements](#requirements)
|
|
42
|
+
- [Documentation](#documentation)
|
|
43
|
+
- [Changelog](#changelog)
|
|
44
|
+
- [License](#license)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## What is StellaLib?
|
|
49
|
+
|
|
50
|
+
**StellaLib** is a TypeScript client library that connects your Discord bot to [Lavalink](https://github.com/lavalink-devs/Lavalink) — a standalone audio server that handles music playback, search, and streaming. StellaLib manages the entire lifecycle: connecting to Lavalink nodes, creating guild-level players, searching tracks, controlling playback, and handling events.
|
|
51
|
+
|
|
52
|
+
Unlike other Lavalink clients, StellaLib:
|
|
53
|
+
|
|
54
|
+
- **Auto-detects** whether your Lavalink server is v3 or v4 and adapts automatically
|
|
55
|
+
- **Persists sessions and player state** across bot restarts so music keeps playing — autoplay, queue, filters, and history all survive
|
|
56
|
+
- **Has a smart autoplay engine** that picks the best next track based on listening history
|
|
57
|
+
- **Auto-failover** — when a node dies, players move to healthy nodes automatically
|
|
58
|
+
- **Proactive health monitoring** — detects overloaded nodes and migrates players *before* they crash
|
|
59
|
+
- **Handles failures gracefully** with fast first reconnect, exponential backoff, rate limit retries, and search fallback
|
|
60
|
+
|
|
61
|
+
## How it Works
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
┌──────────────┐ raw voice events ┌──────────────────┐ WebSocket/REST ┌──────────┐
|
|
65
|
+
│ Discord.js │ ──────────────────────► │ StellaManager │ ◄──────────────────► │ Lavalink │
|
|
66
|
+
│ (your bot) │ ◄────── send payloads ── │ │ │ Server │
|
|
67
|
+
└──────────────┘ └──────────────────┘ └──────────┘
|
|
68
|
+
│
|
|
69
|
+
┌─────────────┼─────────────┐
|
|
70
|
+
▼ ▼ ▼
|
|
71
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
72
|
+
│ Node 1 │ │ Node 2 │ │ Node N │
|
|
73
|
+
│ (v4 auto)│ │ (v3 auto)│ │ │
|
|
74
|
+
└──────────┘ └──────────┘ └──────────┘
|
|
75
|
+
│
|
|
76
|
+
┌─────┼─────┐
|
|
77
|
+
▼ ▼ ▼
|
|
78
|
+
┌────────┐ ┌────────┐
|
|
79
|
+
│Player A│ │Player B│ (one per guild)
|
|
80
|
+
│ Queue │ │ Queue │
|
|
81
|
+
│Filters │ │Filters │
|
|
82
|
+
└────────┘ └────────┘
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Flow:**
|
|
86
|
+
|
|
87
|
+
1. Your bot receives raw Discord voice events and forwards them to `StellaManager`
|
|
88
|
+
2. The Manager routes voice data to the correct `Node` (Lavalink server connection)
|
|
89
|
+
3. Each Node auto-detects its Lavalink version (v3 or v4) and adapts its protocol
|
|
90
|
+
4. `Player` instances (one per guild) handle playback, queue, volume, and filters
|
|
91
|
+
5. The Node's `Rest` client handles track loading, player updates, and session management
|
|
92
|
+
6. Events flow back from Lavalink → Node → Manager → your bot's event handlers
|
|
93
|
+
|
|
94
|
+
## Architecture
|
|
95
|
+
|
|
96
|
+
StellaLib is composed of several core classes that work together:
|
|
97
|
+
|
|
98
|
+
| Class | What it does |
|
|
99
|
+
|---|---|
|
|
100
|
+
| **`StellaManager`** | The entry point. Manages all nodes and players, handles search, voice state updates, caching, and shutdown. You create one Manager per bot. |
|
|
101
|
+
| **`StellaNode`** | Represents a connection to a single Lavalink server. Handles WebSocket connection, heartbeat, reconnect, version detection, session resume, and autoplay logic. |
|
|
102
|
+
| **`StellaPlayer`** | One player per Discord guild. Controls playback (`play`, `pause`, `stop`, `seek`), manages the queue, applies filters, and handles voice readiness. |
|
|
103
|
+
| **`StellaQueue`** | Extends `Array` with music-specific methods: `add()`, `remove()`, `clear()`, `shuffle()`, repeat modes, and `current`/`previous` track tracking. |
|
|
104
|
+
| **`StellaRest`** | HTTP client for Lavalink's REST API. Version-aware (v3 vs v4 endpoints), with rate limit retry, request deduplication, and timeout handling. |
|
|
105
|
+
| **`StellaFilters`** | Manages audio filters and equalizer settings per player. Built-in presets for common effects. |
|
|
106
|
+
| **`LRUCache`** | Bounded least-recently-used cache with TTL expiry for search results. Reduces redundant API calls. |
|
|
107
|
+
| **`FileSessionStore`** | Persists Lavalink session IDs **and full player states** to a JSON file. Enables seamless resume after bot restarts — including autoplay, queue, and filters. |
|
|
108
|
+
|
|
109
|
+
### Project Structure
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
src/
|
|
113
|
+
Structures/
|
|
114
|
+
Manager.ts — Main hub: nodes, players, search, voice, cache, shutdown
|
|
115
|
+
Node.ts — Lavalink node: WS, heartbeat, reconnect, version detect, autoplay
|
|
116
|
+
Player.ts — Guild player: playback, queue, voice ready, filters, move node
|
|
117
|
+
Queue.ts — Queue: add/remove/shuffle, repeat modes, current/previous
|
|
118
|
+
Rest.ts — REST client: version-aware endpoints, retry, dedup, timeout
|
|
119
|
+
Filters.ts — Audio filter management and presets
|
|
120
|
+
LRUCache.ts — Bounded LRU cache with TTL and memory estimation
|
|
121
|
+
SessionStore.ts — FileSessionStore for session persistence
|
|
122
|
+
Types.ts — All TypeScript interfaces, types, and event definitions
|
|
123
|
+
Utils.ts — TrackUtils (build/validate tracks), Structure, Plugin system
|
|
124
|
+
Utils/
|
|
125
|
+
FiltersEqualizers.ts — Equalizer band presets for each filter
|
|
126
|
+
ManagerCheck.ts — Manager option validation
|
|
127
|
+
NodeCheck.ts — Node option validation
|
|
128
|
+
PlayerCheck.ts — Player option validation
|
|
129
|
+
index.ts — Re-exports everything
|
|
130
|
+
```
|
|
25
131
|
|
|
26
132
|
## Installation
|
|
27
133
|
|
|
28
134
|
```bash
|
|
29
|
-
npm install stellalib
|
|
135
|
+
npm install @stella_project/stellalib
|
|
30
136
|
# or
|
|
31
|
-
yarn add stellalib
|
|
137
|
+
yarn add @stella_project/stellalib
|
|
32
138
|
# or
|
|
33
|
-
bun add stellalib
|
|
139
|
+
bun add @stella_project/stellalib
|
|
34
140
|
```
|
|
35
141
|
|
|
36
142
|
## Quick Start
|
|
37
143
|
|
|
38
144
|
```ts
|
|
39
145
|
import { Client, GatewayIntentBits } from "discord.js";
|
|
40
|
-
import { StellaManager, FileSessionStore } from "stellalib";
|
|
146
|
+
import { StellaManager, FileSessionStore } from "@stella_project/stellalib";
|
|
41
147
|
|
|
148
|
+
// 1. Create your Discord client
|
|
42
149
|
const client = new Client({
|
|
43
150
|
intents: [
|
|
44
151
|
GatewayIntentBits.Guilds,
|
|
@@ -48,38 +155,73 @@ const client = new Client({
|
|
|
48
155
|
],
|
|
49
156
|
});
|
|
50
157
|
|
|
158
|
+
// 2. Create the StellaLib manager
|
|
51
159
|
const manager = new StellaManager({
|
|
52
160
|
nodes: [
|
|
53
161
|
{
|
|
54
|
-
identifier: "main",
|
|
55
|
-
host: "localhost",
|
|
56
|
-
port: 2333,
|
|
57
|
-
password: "youshallnotpass"
|
|
58
|
-
resumeStatus: true,
|
|
59
|
-
resumeTimeout: 120,
|
|
60
|
-
heartbeatInterval: 30000,
|
|
162
|
+
identifier: "main", // Unique name for this node
|
|
163
|
+
host: "localhost", // Lavalink server host
|
|
164
|
+
port: 2333, // Lavalink server port
|
|
165
|
+
password: "youshallnotpass",// Lavalink password
|
|
166
|
+
resumeStatus: true, // Enable session resuming
|
|
167
|
+
resumeTimeout: 120, // Seconds Lavalink waits for reconnect
|
|
168
|
+
heartbeatInterval: 30000, // Ping interval in ms
|
|
61
169
|
},
|
|
62
170
|
],
|
|
63
|
-
autoPlay: true,
|
|
64
|
-
defaultSearchPlatform: "spotify",
|
|
65
|
-
searchFallback: ["soundcloud", "youtube
|
|
66
|
-
sessionStore: new FileSessionStore("./sessions.json"),
|
|
67
|
-
caches: { enabled: true, time: 60000, maxSize: 200 },
|
|
171
|
+
autoPlay: true, // Enable autoplay when queue ends
|
|
172
|
+
defaultSearchPlatform: "spotify", // Default search source
|
|
173
|
+
searchFallback: ["soundcloud", "youtube"], // Fallback if primary fails
|
|
174
|
+
sessionStore: new FileSessionStore("./sessions.json"), // Persist sessions
|
|
175
|
+
caches: { enabled: true, time: 60000, maxSize: 200 }, // Search cache
|
|
68
176
|
send(id, payload) {
|
|
177
|
+
// Required: how to send voice payloads to Discord
|
|
69
178
|
const guild = client.guilds.cache.get(id);
|
|
70
179
|
if (guild) guild.shard.send(payload);
|
|
71
180
|
},
|
|
72
181
|
});
|
|
73
182
|
|
|
74
|
-
// Forward raw Discord events to StellaLib
|
|
183
|
+
// 3. Forward raw Discord events to StellaLib (required for voice)
|
|
75
184
|
client.on("raw", (d) => manager.updateVoiceState(d));
|
|
76
185
|
|
|
186
|
+
// 4. Initialize manager when bot is ready
|
|
77
187
|
client.on("ready", () => {
|
|
78
188
|
console.log(`Bot ready as ${client.user?.tag}`);
|
|
79
189
|
manager.init(client.user!.id);
|
|
80
190
|
});
|
|
81
191
|
|
|
82
|
-
//
|
|
192
|
+
// 5. Handle events
|
|
193
|
+
manager.on("NodeConnect", (node) => {
|
|
194
|
+
console.log(`Connected to ${node.options.identifier} (Lavalink v${node.version})`);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
manager.on("TrackStart", (player, track) => {
|
|
198
|
+
console.log(`Now playing: ${track.title}`);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// 6. Play music (example in a command handler)
|
|
202
|
+
async function play(guildId: string, voiceChannelId: string, query: string) {
|
|
203
|
+
// Create or get player
|
|
204
|
+
let player = manager.players.get(guildId);
|
|
205
|
+
if (!player) {
|
|
206
|
+
player = manager.create({
|
|
207
|
+
guild: guildId,
|
|
208
|
+
voiceChannel: voiceChannelId,
|
|
209
|
+
textChannel: "TEXT_CHANNEL_ID",
|
|
210
|
+
volume: 50,
|
|
211
|
+
selfDeafen: true,
|
|
212
|
+
});
|
|
213
|
+
player.connect();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Search and queue
|
|
217
|
+
const res = await manager.search(query, "USER_ID");
|
|
218
|
+
if (res.tracks.length) {
|
|
219
|
+
player.queue.add(res.tracks[0]);
|
|
220
|
+
if (!player.playing) player.play();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 7. Graceful shutdown
|
|
83
225
|
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
84
226
|
process.on(sig, async () => {
|
|
85
227
|
await manager.shutdown();
|
|
@@ -87,184 +229,561 @@ for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
|
87
229
|
});
|
|
88
230
|
}
|
|
89
231
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const player = manager.create({
|
|
93
|
-
guild: "GUILD_ID",
|
|
94
|
-
voiceChannel: "VOICE_CHANNEL_ID",
|
|
95
|
-
textChannel: "TEXT_CHANNEL_ID",
|
|
96
|
-
});
|
|
97
|
-
player.connect();
|
|
232
|
+
client.login("YOUR_BOT_TOKEN");
|
|
233
|
+
```
|
|
98
234
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
235
|
+
## Core Concepts
|
|
236
|
+
|
|
237
|
+
### Manager
|
|
238
|
+
|
|
239
|
+
`StellaManager` is the central hub. You create **one instance** and it manages everything.
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const manager = new StellaManager({
|
|
243
|
+
nodes: [...], // Array of Lavalink node configs
|
|
244
|
+
send: (id, payload) => { ... }, // How to send to Discord gateway
|
|
245
|
+
autoPlay: true, // Auto-play next track when queue ends
|
|
246
|
+
defaultSearchPlatform: "spotify",
|
|
247
|
+
searchFallback: ["soundcloud", "youtube music"],
|
|
248
|
+
sessionStore: new FileSessionStore("./sessions.json"),
|
|
249
|
+
caches: { enabled: true, time: 60000, maxSize: 200 },
|
|
250
|
+
clientName: "StellaLib",
|
|
251
|
+
shards: 1,
|
|
104
252
|
});
|
|
105
253
|
|
|
106
|
-
client
|
|
254
|
+
// Initialize after Discord client is ready
|
|
255
|
+
manager.init(client.user!.id);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Key methods:**
|
|
259
|
+
- `manager.init(clientId)` — Connect all nodes
|
|
260
|
+
- `manager.create(options)` — Create a player for a guild
|
|
261
|
+
- `manager.get(guildId)` — Get existing player
|
|
262
|
+
- `manager.search(query, requester?)` — Search tracks with fallback
|
|
263
|
+
- `manager.updateVoiceState(data)` — Forward raw Discord voice events
|
|
264
|
+
- `manager.shutdown()` — Gracefully close everything
|
|
265
|
+
- `manager.getStats()` — Get node/player/cache statistics
|
|
266
|
+
|
|
267
|
+
### Node
|
|
268
|
+
|
|
269
|
+
`StellaNode` represents a single Lavalink server connection. Nodes are created automatically from the `nodes` config.
|
|
270
|
+
|
|
271
|
+
**What it does automatically:**
|
|
272
|
+
- Detects Lavalink version (v3 or v4) before connecting
|
|
273
|
+
- Establishes WebSocket with the correct URL and headers
|
|
274
|
+
- Sends heartbeat pings to detect dead connections
|
|
275
|
+
- Reconnects with exponential backoff + jitter on disconnect
|
|
276
|
+
- Configures session resuming (v3: WS op, v4: REST PATCH)
|
|
277
|
+
- Syncs player state after resume (v4 only)
|
|
278
|
+
- Handles autoplay logic when queue ends
|
|
279
|
+
|
|
280
|
+
**Properties:**
|
|
281
|
+
- `node.version` — Detected Lavalink version (`3` or `4`)
|
|
282
|
+
- `node.connected` — Whether WebSocket is open
|
|
283
|
+
- `node.stats` — CPU, memory, players, uptime stats
|
|
284
|
+
- `node.info` — Cached Lavalink server info (plugins, sources)
|
|
285
|
+
- `node.penalties` — Calculated penalty score for load balancing
|
|
286
|
+
|
|
287
|
+
### Player
|
|
288
|
+
|
|
289
|
+
`StellaPlayer` controls playback for **one Discord guild**. Created via `manager.create()`.
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
const player = manager.create({
|
|
293
|
+
guild: "GUILD_ID",
|
|
294
|
+
voiceChannel: "VOICE_CHANNEL_ID",
|
|
295
|
+
textChannel: "TEXT_CHANNEL_ID",
|
|
296
|
+
volume: 50,
|
|
297
|
+
selfDeafen: true,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
player.connect(); // Join voice channel
|
|
301
|
+
player.play(); // Play first track in queue
|
|
302
|
+
player.pause(true); // Pause
|
|
303
|
+
player.pause(false); // Resume
|
|
304
|
+
player.stop(); // Stop current track (plays next)
|
|
305
|
+
player.seek(30000); // Seek to 30 seconds
|
|
306
|
+
player.setVolume(80); // Set volume (0-100)
|
|
307
|
+
player.setTrackRepeat(true); // Repeat current track
|
|
308
|
+
player.setQueueRepeat(true); // Repeat entire queue
|
|
309
|
+
player.setAutoplay(true, botUser); // Enable smart autoplay
|
|
310
|
+
player.moveNode("other-node"); // Move to another Lavalink node
|
|
311
|
+
player.destroy(); // Leave channel and clean up
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Queue
|
|
315
|
+
|
|
316
|
+
`StellaQueue` extends JavaScript's `Array` with music-specific helpers:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
player.queue.add(track); // Add track(s) to end
|
|
320
|
+
player.queue.add([track1, track2]); // Add multiple
|
|
321
|
+
player.queue.remove(0); // Remove by index
|
|
322
|
+
player.queue.clear(); // Clear all queued tracks
|
|
323
|
+
player.queue.shuffle(); // Randomize order
|
|
324
|
+
player.queue.current; // Currently playing track
|
|
325
|
+
player.queue.previous; // Previously played track
|
|
326
|
+
player.queue.totalSize; // current + queued count
|
|
327
|
+
player.queue.size; // Queued count (excluding current)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Rest
|
|
331
|
+
|
|
332
|
+
`StellaRest` handles all HTTP communication with Lavalink. It's version-aware — the same method call works for both v3 and v4.
|
|
333
|
+
|
|
334
|
+
| Method | v3 behavior | v4 behavior |
|
|
335
|
+
|---|---|---|
|
|
336
|
+
| `loadTracks(id)` | `GET /loadtracks` → normalized | `GET /v4/loadtracks` |
|
|
337
|
+
| `updatePlayer(opts)` | WS ops (`play`, `pause`, etc.) | `PATCH /v4/sessions/.../players/...` |
|
|
338
|
+
| `destroyPlayer(id)` | WS `destroy` op | `DELETE /v4/sessions/.../players/...` |
|
|
339
|
+
| `configureResume(t)` | WS `configureResuming` op | `PATCH /v4/sessions/...` |
|
|
340
|
+
| `getInfo()` | `GET /version` | `GET /v4/info` |
|
|
341
|
+
| `decodeTracks(arr)` | `POST /decodetracks` | `POST /v4/decodetracks` |
|
|
342
|
+
|
|
343
|
+
**Built-in resilience:**
|
|
344
|
+
- Auto-retry on 429 rate limits (up to 3 retries with `Retry-After`)
|
|
345
|
+
- GET request deduplication (concurrent identical GETs share one request)
|
|
346
|
+
- Configurable request timeout
|
|
347
|
+
- Request/failure counters
|
|
348
|
+
|
|
349
|
+
### Filters
|
|
350
|
+
|
|
351
|
+
Built-in audio filter presets:
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
await player.filters.setFilter("bassboost", true);
|
|
355
|
+
await player.filters.setFilter("nightcore", true);
|
|
356
|
+
await player.filters.setFilter("vaporwave", true);
|
|
357
|
+
await player.filters.setFilter("eightD", true);
|
|
358
|
+
await player.filters.setFilter("slowmo", true);
|
|
359
|
+
await player.filters.setFilter("soft", true);
|
|
360
|
+
await player.filters.setFilter("trebleBass", true);
|
|
361
|
+
await player.filters.setFilter("tv", true);
|
|
362
|
+
await player.filters.setFilter("distort", true);
|
|
363
|
+
|
|
364
|
+
await player.filters.clearFilters(); // Remove all
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Each preset applies specific equalizer bands, timescale, rotation, or other Lavalink audio parameters.
|
|
368
|
+
|
|
369
|
+
## Multi-Version Support (v3 + v4)
|
|
370
|
+
|
|
371
|
+
StellaLib **automatically detects** your Lavalink server version before connecting. No configuration needed.
|
|
372
|
+
|
|
373
|
+
**How detection works:**
|
|
374
|
+
1. Before WebSocket connect, the Node sends `GET /v4/info` to the server
|
|
375
|
+
2. If it responds `200 OK` → **Lavalink v4** detected (server info is cached)
|
|
376
|
+
3. If it fails, tries `GET /version` → **Lavalink v3** detected
|
|
377
|
+
4. Falls back to v4 if both fail
|
|
378
|
+
|
|
379
|
+
**What adapts automatically:**
|
|
380
|
+
|
|
381
|
+
| Aspect | Lavalink v3 | Lavalink v4 |
|
|
382
|
+
|---|---|---|
|
|
383
|
+
| **WebSocket URL** | `ws://host:port` | `ws://host:port/v4/websocket` |
|
|
384
|
+
| **Player control** | WebSocket ops (`play`, `stop`, `pause`, `seek`, `volume`, `filters`) | REST `PATCH` |
|
|
385
|
+
| **Session resume** | `Resume-Key` header + WS `configureResuming` | `Session-Id` header + REST `PATCH` |
|
|
386
|
+
| **Track loading** | `/loadtracks` (response normalized to v4 format) | `/v4/loadtracks` |
|
|
387
|
+
| **Server info** | `/version` (returns version string) | `/v4/info` (returns full info JSON) |
|
|
388
|
+
| **Player sync** | Not available (v3 limitation) | Full player state sync on resume |
|
|
389
|
+
| **Track data** | `track` field → mapped to `encoded` | `encoded` field |
|
|
390
|
+
| **Load types** | `TRACK_LOADED` → `track`, `SEARCH_RESULT` → `search`, etc. | Already v4 format |
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
manager.on("NodeConnect", (node) => {
|
|
394
|
+
console.log(`Lavalink v${node.version}`); // 3 or 4
|
|
395
|
+
});
|
|
107
396
|
```
|
|
108
397
|
|
|
109
398
|
## Session Persistence
|
|
110
399
|
|
|
111
|
-
StellaLib
|
|
400
|
+
StellaLib persists session IDs so music **keeps playing** after bot restarts.
|
|
112
401
|
|
|
113
402
|
```ts
|
|
114
|
-
import { FileSessionStore } from "stellalib";
|
|
403
|
+
import { FileSessionStore } from "@stella_project/stellalib";
|
|
115
404
|
|
|
116
405
|
const manager = new StellaManager({
|
|
117
406
|
sessionStore: new FileSessionStore("./sessions.json"),
|
|
118
407
|
nodes: [{
|
|
119
|
-
resumeStatus: true,
|
|
120
|
-
resumeTimeout: 120,
|
|
408
|
+
resumeStatus: true, // Tell Lavalink to hold the session
|
|
409
|
+
resumeTimeout: 120, // Seconds to wait before destroying session
|
|
121
410
|
// ...
|
|
122
411
|
}],
|
|
123
412
|
// ...
|
|
124
413
|
});
|
|
125
|
-
|
|
126
|
-
// On shutdown, sessions are saved automatically
|
|
127
|
-
await manager.shutdown();
|
|
128
414
|
```
|
|
129
415
|
|
|
130
|
-
|
|
416
|
+
**How it works:**
|
|
417
|
+
1. On connect, Node loads saved session ID from the store
|
|
418
|
+
2. Sends it as `Session-Id` (v4) or `Resume-Key` (v3) header
|
|
419
|
+
3. Lavalink resumes the session — players keep their state
|
|
420
|
+
4. On disconnect/shutdown, session ID **and full player state** is persisted to the store
|
|
421
|
+
5. On resume, autoplay state, queue, filters, history, and seed pool are all restored
|
|
422
|
+
|
|
423
|
+
**Custom stores** (e.g., Redis, database):
|
|
131
424
|
|
|
132
425
|
```ts
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
426
|
+
const manager = new StellaManager({
|
|
427
|
+
sessionStore: {
|
|
428
|
+
async get(nodeId) { return await redis.get(`session:${nodeId}`); },
|
|
429
|
+
async set(nodeId, sessionId) { await redis.set(`session:${nodeId}`, sessionId); },
|
|
430
|
+
async delete(nodeId) { await redis.del(`session:${nodeId}`); },
|
|
431
|
+
},
|
|
432
|
+
// ...
|
|
433
|
+
});
|
|
138
434
|
```
|
|
139
435
|
|
|
140
|
-
##
|
|
436
|
+
## Player State Persistence
|
|
141
437
|
|
|
142
|
-
|
|
438
|
+
StellaLib v1.1.0+ persists **full player state** — not just session IDs — across bot restarts. This means autoplay, queue, filters, repeat modes, and listening history all survive a restart.
|
|
143
439
|
|
|
144
440
|
```ts
|
|
145
|
-
|
|
146
|
-
player.setAutoplay(true, { id: user.id, tag: user.tag });
|
|
147
|
-
```
|
|
441
|
+
import { FileSessionStore } from "@stella_project/stellalib";
|
|
148
442
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
443
|
+
// FileSessionStore automatically handles both session IDs and player states
|
|
444
|
+
const manager = new StellaManager({
|
|
445
|
+
sessionStore: new FileSessionStore("./sessions.json"),
|
|
446
|
+
// Player state store is auto-detected from FileSessionStore
|
|
447
|
+
// Or provide a custom one:
|
|
448
|
+
// playerStateStore: myCustomStore,
|
|
449
|
+
// ...
|
|
450
|
+
});
|
|
451
|
+
```
|
|
155
452
|
|
|
156
|
-
|
|
453
|
+
**What is persisted per player:**
|
|
454
|
+
- Autoplay on/off state and bot user ID
|
|
455
|
+
- Autoplay history (last 50 tracks) and seed pool
|
|
456
|
+
- Queue (all tracks with encoded data)
|
|
457
|
+
- Filter configuration and active preset flags
|
|
458
|
+
- Repeat modes (track, queue, dynamic)
|
|
459
|
+
- Volume, voice channel, text channel
|
|
157
460
|
|
|
158
|
-
|
|
461
|
+
**Custom player state store** (e.g., Redis):
|
|
159
462
|
|
|
160
463
|
```ts
|
|
161
464
|
const manager = new StellaManager({
|
|
162
|
-
|
|
163
|
-
|
|
465
|
+
playerStateStore: {
|
|
466
|
+
async getPlayerState(guildId) { return JSON.parse(await redis.get(`player:${guildId}`)); },
|
|
467
|
+
async setPlayerState(guildId, state) { await redis.set(`player:${guildId}`, JSON.stringify(state)); },
|
|
468
|
+
async deletePlayerState(guildId) { await redis.del(`player:${guildId}`); },
|
|
469
|
+
async getAllPlayerStates() { /* return all states */ },
|
|
470
|
+
},
|
|
164
471
|
// ...
|
|
165
472
|
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
## Auto-Failover
|
|
476
|
+
|
|
477
|
+
When a Lavalink node goes down **mid-playback**, StellaLib **immediately** moves all playing/paused players to a healthy node — audio continues at the exact same position with typically <150ms gap:
|
|
166
478
|
|
|
167
|
-
|
|
168
|
-
|
|
479
|
+
```
|
|
480
|
+
Node A crashes! 💥
|
|
481
|
+
|
|
482
|
+
t=0ms WebSocket close fires
|
|
483
|
+
t=2ms attemptSeamlessFailover() starts
|
|
484
|
+
t=5ms Healthy nodes sorted by penalty score
|
|
485
|
+
t=50ms Voice state sent to Node B
|
|
486
|
+
t=100ms Track + position + filters sent
|
|
487
|
+
t=150ms Audio resumes on Node B ♪
|
|
169
488
|
```
|
|
170
489
|
|
|
171
|
-
|
|
490
|
+
```
|
|
491
|
+
Node A (dies) Node B (healthy) Node C (healthy)
|
|
492
|
+
Player 1 ──────────────► Player 1 ♪
|
|
493
|
+
Player 2 ──────────────► Player 2 ♪ (load balanced)
|
|
494
|
+
Player 3 ────────────────────────────────────► Player 3 ♪
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Three Layers of Protection
|
|
498
|
+
|
|
499
|
+
| Layer | Trigger | Speed |
|
|
500
|
+
|---|---|---|
|
|
501
|
+
| **Seamless failover** | Node unexpectedly disconnects | Immediate (<150ms) |
|
|
502
|
+
| **Health monitoring** | CPU/frame deficit exceeds threshold | Proactive (before crash) |
|
|
503
|
+
| **Destroy failover** | Node explicitly removed from pool | Immediate |
|
|
504
|
+
|
|
505
|
+
### PlayerFailover Event
|
|
172
506
|
|
|
173
507
|
```ts
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
508
|
+
manager.on("PlayerFailover", (player, oldNode, newNode) => {
|
|
509
|
+
console.log(`Player ${player.guild} moved: ${oldNode} → ${newNode}`);
|
|
510
|
+
// Optionally notify the guild
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
- Players are distributed across healthy nodes by **penalty score** (not all dumped on one node)
|
|
515
|
+
- If no healthy nodes exist, players wait for reconnect (fast 2s retry on first attempt)
|
|
516
|
+
- See [docs/13-seamless-failover.md](docs/13-seamless-failover.md) for full architecture details
|
|
517
|
+
|
|
518
|
+
## Inactivity Timeout
|
|
519
|
+
|
|
520
|
+
Auto-disconnect the bot when it's alone in a voice channel:
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
const player = manager.create({
|
|
524
|
+
guild: guildId,
|
|
525
|
+
voiceChannel: voiceChannelId,
|
|
526
|
+
inactivityTimeout: 300000, // 5 minutes
|
|
527
|
+
});
|
|
179
528
|
|
|
180
|
-
//
|
|
181
|
-
|
|
529
|
+
// In your voiceStateUpdate handler:
|
|
530
|
+
client.on("voiceStateUpdate", (oldState, newState) => {
|
|
531
|
+
const player = manager.get(oldState.guild.id);
|
|
532
|
+
if (!player) return;
|
|
533
|
+
|
|
534
|
+
const channel = oldState.guild.channels.cache.get(player.voiceChannel!);
|
|
535
|
+
const members = channel?.members?.filter((m) => !m.user.bot).size ?? 0;
|
|
536
|
+
|
|
537
|
+
if (members === 0) {
|
|
538
|
+
player.startInactivityTimer(); // Start countdown
|
|
539
|
+
} else {
|
|
540
|
+
player.stopInactivityTimer(); // Cancel — someone joined
|
|
541
|
+
}
|
|
542
|
+
});
|
|
182
543
|
```
|
|
183
544
|
|
|
184
|
-
|
|
545
|
+
## Queue Limits & Deduplication
|
|
546
|
+
|
|
547
|
+
### Max Queue Size
|
|
185
548
|
|
|
186
|
-
|
|
549
|
+
Prevent memory abuse by limiting the queue:
|
|
187
550
|
|
|
188
551
|
```ts
|
|
189
|
-
const
|
|
190
|
-
|
|
552
|
+
const player = manager.create({
|
|
553
|
+
guild: guildId,
|
|
554
|
+
voiceChannel: voiceChannelId,
|
|
555
|
+
maxQueueSize: 500, // Max 500 tracks in queue
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// Check before adding
|
|
559
|
+
if (!player.canAddToQueue(tracks.length)) {
|
|
560
|
+
return message.reply(`Queue is full! Only ${player.queueSpaceRemaining} slots left.`);
|
|
561
|
+
}
|
|
562
|
+
player.queue.add(tracks); // Excess tracks are automatically truncated
|
|
191
563
|
```
|
|
192
564
|
|
|
193
|
-
|
|
565
|
+
### Track Deduplication
|
|
566
|
+
|
|
567
|
+
Prevent the same song from being queued twice:
|
|
194
568
|
|
|
195
569
|
```ts
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
manager.on("SocketClosed", (player, payload) => { });
|
|
207
|
-
manager.on("PlayerCreate", (player) => { });
|
|
208
|
-
manager.on("PlayerDestroy", (player) => { });
|
|
209
|
-
manager.on("PlayerMove", (player, oldChannel, newChannel) => { });
|
|
210
|
-
manager.on("PlayerDisconnect", (player, oldChannel) => { });
|
|
211
|
-
manager.on("PlayerStateUpdate", (oldPlayer, newPlayer) => { });
|
|
212
|
-
manager.on("Debug", (message) => { });
|
|
570
|
+
player.queue.noDuplicates = true;
|
|
571
|
+
|
|
572
|
+
// Now queue.add() silently skips tracks that are already queued
|
|
573
|
+
player.queue.add(track); // Added
|
|
574
|
+
player.queue.add(track); // Silently skipped (same URI)
|
|
575
|
+
|
|
576
|
+
// Check manually:
|
|
577
|
+
if (player.queue.isDuplicate(track)) {
|
|
578
|
+
return message.reply("That track is already in the queue!");
|
|
579
|
+
}
|
|
213
580
|
```
|
|
214
581
|
|
|
215
|
-
##
|
|
582
|
+
## Node Health Monitoring
|
|
583
|
+
|
|
584
|
+
StellaLib can proactively monitor node health and migrate players **before** a node crashes:
|
|
216
585
|
|
|
586
|
+
```ts
|
|
587
|
+
const manager = new StellaManager({
|
|
588
|
+
nodeHealthThresholds: {
|
|
589
|
+
maxCpuLoad: 0.85, // Migrate when CPU exceeds 85%
|
|
590
|
+
maxFrameDeficit: 300, // Migrate when frame deficit exceeds 300
|
|
591
|
+
checkInterval: 30000, // Check every 30 seconds
|
|
592
|
+
},
|
|
593
|
+
// ...
|
|
594
|
+
});
|
|
217
595
|
```
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
LRUCache.ts — Bounded LRU cache with TTL
|
|
227
|
-
SessionStore.ts — FileSessionStore for session persistence
|
|
228
|
-
Types.ts — All TypeScript interfaces and types
|
|
229
|
-
Utils.ts — TrackUtils, Structure, Plugin helpers
|
|
230
|
-
Utils/
|
|
231
|
-
FiltersEqualizers.ts — Equalizer band presets
|
|
232
|
-
ManagerCheck.ts — Manager option validation
|
|
233
|
-
NodeCheck.ts — Node option validation
|
|
234
|
-
PlayerCheck.ts — Player option validation
|
|
235
|
-
index.ts — Re-exports everything
|
|
596
|
+
|
|
597
|
+
```
|
|
598
|
+
Health Check (every 30s)
|
|
599
|
+
│
|
|
600
|
+
Node A: CPU 92% ──► OVERLOADED
|
|
601
|
+
Node B: CPU 40% ──► healthy
|
|
602
|
+
│
|
|
603
|
+
Migrate players A → B (preemptive)
|
|
236
604
|
```
|
|
237
605
|
|
|
238
|
-
|
|
606
|
+
This is **proactive** failover — it moves players before they experience audio issues, unlike the reactive auto-failover which only triggers when a node dies.
|
|
607
|
+
|
|
608
|
+
## Smart Autoplay
|
|
239
609
|
|
|
240
|
-
StellaLib auto-
|
|
610
|
+
When the queue ends and autoplay is enabled, StellaLib's auto-mix engine picks the best next track.
|
|
241
611
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
612
|
+
```ts
|
|
613
|
+
player.setAutoplay(true, client.user);
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**How the engine works:**
|
|
617
|
+
|
|
618
|
+
1. **Seed collection** — Gathers the last 5 played tracks as context seeds
|
|
619
|
+
2. **Source detection** — Identifies if the listener was on Spotify, YouTube, or SoundCloud
|
|
620
|
+
3. **Recommendation fetch** — Uses Spotify `sprec:` (seed artists + seed tracks) or YouTube Mix
|
|
621
|
+
4. **Candidate scoring** — Each candidate is scored on:
|
|
622
|
+
- Duration similarity to recent tracks
|
|
623
|
+
- Author/title keyword overlap
|
|
624
|
+
- Remix/cover penalty (avoids non-originals)
|
|
625
|
+
- History check (never replays last 50 tracks)
|
|
626
|
+
5. **Best transition** — Picks the highest-scoring candidate
|
|
627
|
+
6. **Cross-platform mirror** — If needed, re-searches on SoundCloud/YouTube for a streamable version
|
|
628
|
+
7. **Fallback chain** — If recommendations fail, tries theme-based search, then random from same artist
|
|
629
|
+
|
|
630
|
+
## Search with Fallback
|
|
250
631
|
|
|
251
632
|
```ts
|
|
252
|
-
// No configuration needed — version is auto-detected
|
|
253
633
|
const manager = new StellaManager({
|
|
254
|
-
|
|
634
|
+
defaultSearchPlatform: "spotify",
|
|
635
|
+
searchFallback: ["soundcloud", "youtube music", "youtube"],
|
|
255
636
|
// ...
|
|
256
637
|
});
|
|
257
638
|
|
|
258
|
-
//
|
|
259
|
-
manager.
|
|
260
|
-
|
|
261
|
-
|
|
639
|
+
// Searches Spotify first. If empty, tries SoundCloud, then YouTube Music, then YouTube.
|
|
640
|
+
const result = await manager.search("natori セレナーデ", userId);
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Supported platforms:** `spotify`, `soundcloud`, `youtube`, `youtube music`, `deezer`, `tidal`, `applemusic`, `bandcamp`, `jiosaavn`
|
|
644
|
+
|
|
645
|
+
## Audio Filters
|
|
646
|
+
|
|
647
|
+
| Filter | Effect |
|
|
648
|
+
|---|---|
|
|
649
|
+
| `bassboost` | Boosts low frequencies |
|
|
650
|
+
| `nightcore` | Speeds up + higher pitch |
|
|
651
|
+
| `vaporwave` | Slows down + lower pitch |
|
|
652
|
+
| `eightD` | Rotating stereo panning |
|
|
653
|
+
| `slowmo` | Slower playback speed |
|
|
654
|
+
| `soft` | Reduces harsh frequencies |
|
|
655
|
+
| `trebleBass` | Boosts both high and low bands |
|
|
656
|
+
| `tv` | Tinny speaker simulation |
|
|
657
|
+
| `distort` | Audio distortion effect |
|
|
658
|
+
|
|
659
|
+
## Events Reference
|
|
660
|
+
|
|
661
|
+
| Event | Parameters | Description |
|
|
662
|
+
|---|---|---|
|
|
663
|
+
| `NodeCreate` | `(node)` | Node instance created |
|
|
664
|
+
| `NodeConnect` | `(node)` | WebSocket connection established |
|
|
665
|
+
| `NodeReconnect` | `(node)` | Attempting reconnection |
|
|
666
|
+
| `NodeDisconnect` | `(node, reason)` | WebSocket disconnected |
|
|
667
|
+
| `NodeDestroy` | `(node)` | Node destroyed |
|
|
668
|
+
| `NodeError` | `(node, error)` | Error on node |
|
|
669
|
+
| `NodeRaw` | `(payload)` | Raw WebSocket message |
|
|
670
|
+
| `TrackStart` | `(player, track, payload)` | Track started playing |
|
|
671
|
+
| `TrackEnd` | `(player, track, payload)` | Track finished |
|
|
672
|
+
| `TrackStuck` | `(player, track, payload)` | Track got stuck |
|
|
673
|
+
| `TrackError` | `(player, track, payload)` | Track playback error |
|
|
674
|
+
| `QueueEnd` | `(player, track, payload)` | Queue finished (all tracks played) |
|
|
675
|
+
| `PlayerCreate` | `(player)` | Player created for a guild |
|
|
676
|
+
| `PlayerDestroy` | `(player)` | Player destroyed |
|
|
677
|
+
| `PlayerMove` | `(player, oldChannel, newChannel)` | Bot moved to different voice channel |
|
|
678
|
+
| `PlayerDisconnect` | `(player, oldChannel)` | Bot disconnected from voice |
|
|
679
|
+
| `PlayerStateUpdate` | `(oldPlayer, newPlayer)` | Player state changed |
|
|
680
|
+
| `PlayerFailover` | `(player, oldNode, newNode)` | Player seamlessly moved to a new node |
|
|
681
|
+
| `SocketClosed` | `(player, payload)` | Discord voice WebSocket closed for player |
|
|
682
|
+
| `Debug` | `(message)` | Debug log message |
|
|
683
|
+
|
|
684
|
+
## Configuration Reference
|
|
685
|
+
|
|
686
|
+
### Manager Options
|
|
687
|
+
|
|
688
|
+
```ts
|
|
689
|
+
interface ManagerOptions {
|
|
690
|
+
nodes: NodeOptions[]; // Lavalink server configs (required)
|
|
691
|
+
send: (id: string, payload: Payload) => void; // Discord gateway send (required)
|
|
692
|
+
clientId?: string; // Bot user ID (set by init())
|
|
693
|
+
clientName?: string; // Client identifier sent to Lavalink
|
|
694
|
+
shards?: number; // Shard count
|
|
695
|
+
autoPlay?: boolean; // Enable autoplay on queue end
|
|
696
|
+
defaultSearchPlatform?: SearchPlatform;// Default search source
|
|
697
|
+
searchFallback?: string[]; // Fallback platforms
|
|
698
|
+
sessionStore?: SessionStore; // Session persistence store
|
|
699
|
+
playerStateStore?: PlayerStateStore; // Full player state persistence (auto-detected from sessionStore)
|
|
700
|
+
nodeHealthThresholds?: { // Proactive node health monitoring
|
|
701
|
+
maxCpuLoad?: number; // Max CPU load (0-1), default: 0.9
|
|
702
|
+
maxFrameDeficit?: number; // Max frame deficit, default: 500
|
|
703
|
+
checkInterval?: number; // Check interval (ms), default: 60000
|
|
704
|
+
};
|
|
705
|
+
caches?: {
|
|
706
|
+
enabled: boolean;
|
|
707
|
+
time: number; // TTL in ms
|
|
708
|
+
maxSize: number; // Max cached entries
|
|
709
|
+
};
|
|
710
|
+
plugins?: Plugin[]; // Custom plugins
|
|
711
|
+
}
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
### Node Options
|
|
715
|
+
|
|
716
|
+
```ts
|
|
717
|
+
interface NodeOptions {
|
|
718
|
+
host: string; // Lavalink host
|
|
719
|
+
port: number; // Lavalink port
|
|
720
|
+
password: string; // Lavalink password
|
|
721
|
+
identifier?: string; // Unique node name
|
|
722
|
+
secure?: boolean; // Use wss:// and https://
|
|
723
|
+
retryAmount?: number; // Max reconnect attempts
|
|
724
|
+
retryDelay?: number; // Base delay between retries (ms)
|
|
725
|
+
requestTimeout?: number; // REST request timeout (ms)
|
|
726
|
+
resumeStatus?: boolean; // Enable session resuming
|
|
727
|
+
resumeTimeout?: number; // Seconds Lavalink holds session
|
|
728
|
+
heartbeatInterval?: number;// WebSocket ping interval (ms)
|
|
729
|
+
}
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### Player Options
|
|
733
|
+
|
|
734
|
+
```ts
|
|
735
|
+
interface PlayerOptions {
|
|
736
|
+
guild: string; // Guild ID (required)
|
|
737
|
+
voiceChannel?: string; // Voice channel ID
|
|
738
|
+
textChannel?: string; // Text channel ID
|
|
739
|
+
node?: string; // Preferred node identifier
|
|
740
|
+
volume?: number; // Initial volume (default: 11)
|
|
741
|
+
selfMute?: boolean; // Self mute in voice
|
|
742
|
+
selfDeafen?: boolean; // Self deafen in voice
|
|
743
|
+
inactivityTimeout?: number; // Auto-disconnect when alone (ms, 0=disabled)
|
|
744
|
+
maxQueueSize?: number; // Max queue tracks (0=unlimited)
|
|
745
|
+
}
|
|
262
746
|
```
|
|
263
747
|
|
|
264
748
|
## Requirements
|
|
265
749
|
|
|
266
750
|
- **Node.js** >= 18.0.0
|
|
267
751
|
- **Lavalink** v3.x or v4.x
|
|
752
|
+
- **Discord.js** v14+ (or any library that exposes raw gateway events)
|
|
753
|
+
|
|
754
|
+
## Documentation
|
|
755
|
+
|
|
756
|
+
For detailed guides and API reference, see the [docs/](docs/) folder:
|
|
757
|
+
|
|
758
|
+
- [Getting Started](docs/01-getting-started.md)
|
|
759
|
+
- [Architecture](docs/02-architecture.md)
|
|
760
|
+
- [Manager](docs/03-manager.md)
|
|
761
|
+
- [Node](docs/04-node.md)
|
|
762
|
+
- [Player](docs/05-player.md)
|
|
763
|
+
- [Queue](docs/06-queue.md)
|
|
764
|
+
- [Events](docs/07-events.md)
|
|
765
|
+
- [Filters](docs/08-filters.md)
|
|
766
|
+
- [Session Persistence](docs/09-session-persistence.md)
|
|
767
|
+
- [Multi-Version Support](docs/10-multi-version.md)
|
|
768
|
+
- [Autoplay Engine](docs/11-autoplay.md)
|
|
769
|
+
- [Player State Persistence](docs/12-player-state-persistence.md)
|
|
770
|
+
|
|
771
|
+
## Changelog
|
|
772
|
+
|
|
773
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes per version.
|
|
774
|
+
|
|
775
|
+
## Credits
|
|
776
|
+
|
|
777
|
+
StellaLib stands on the shoulders of these amazing projects:
|
|
778
|
+
|
|
779
|
+
| Project | Description | Link |
|
|
780
|
+
|---|---|---|
|
|
781
|
+
| **Lavalink** | The audio server that powers everything | [GitHub](https://github.com/lavalink-devs/Lavalink) · [Website](https://lavalink.dev/) |
|
|
782
|
+
| **LithiumX** | Direct upstream — StellaLib is derived from LithiumX by Anantix Network (MIT) | [GitHub](https://github.com/anantix-network/LithiumX) |
|
|
783
|
+
| **Erela.js** | Pioneered the Lavalink client pattern in the JS ecosystem — many design patterns originated here | [GitHub](https://github.com/MenuDocs/erela.js) |
|
|
784
|
+
| **MagmaStream** | Inspiration for advanced features like improved node management and audio quality | [GitHub](https://github.com/Magmastream-NPM/magmastream) |
|
|
785
|
+
|
|
786
|
+
Thank you to all the maintainers and contributors of these projects for making music bots possible.
|
|
268
787
|
|
|
269
788
|
## License
|
|
270
789
|
|