@stella_project/stellalib 1.0.1 → 1.0.21

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.
Files changed (2) hide show
  1. package/README.md +461 -159
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,44 +1,144 @@
1
- # StellaLib
2
-
3
- A powerful, modern Lavalink client library for TypeScript/JavaScript with auto version detection (v3 + v4), session persistence, smart autoplay, and graceful shutdown.
4
-
5
- [![npm version](https://img.shields.io/npm/v/stellalib.svg)](https://www.npmjs.com/package/stellalib)
6
- [![License: OSL-3.0](https://img.shields.io/badge/License-OSL--3.0-blue.svg)](LICENSE)
7
-
8
- ## Features
9
-
10
- - **Lavalink v3 + v4** — Auto-detects server version and adapts protocol automatically
11
- - **Session Persistence** — Save/restore session IDs across bot restarts with `FileSessionStore`
12
- - **Smart Autoplay** — Auto-mix engine with transition scoring, multi-seed recommendations, and history tracking
13
- - **Graceful Shutdown** — Persist sessions, close nodes cleanly, and flush stores on SIGINT/SIGTERM
14
- - **Voice Readiness** — Promise-based voice connection waiting before playback
15
- - **Audio Filters** — Built-in presets: bassboost, nightcore, vaporwave, 8D, slowmo, and more
16
- - **Search Caching** — LRU cache with TTL for search results to reduce API calls
17
- - **Search Fallback** — Automatic fallback across platforms (Spotify → SoundCloud → YouTube)
18
- - **Node Selection** — Penalty-based, least-load, least-players, or priority-based node selection
19
- - **Heartbeat** — WebSocket ping/pong to detect dead connections and auto-reconnect
20
- - **REST Resilience** — Auto-retry on 429 rate limits, GET deduplication, request timeouts
21
- - **Reconnect** — Exponential backoff with jitter to prevent thundering herd
22
- - **Plugin System** — Extensible via plugins
23
- - **Typed Events** — Fully typed event emitter for all manager events
24
- - **Strict TypeScript** — Written with strict TypeScript
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
+ - [Smart Autoplay](#smart-autoplay)
32
+ - [Search with Fallback](#search-with-fallback)
33
+ - [Audio Filters](#audio-filters)
34
+ - [Events Reference](#events-reference)
35
+ - [Configuration Reference](#configuration-reference)
36
+ - [Requirements](#requirements)
37
+ - [Documentation](#documentation)
38
+ - [Changelog](#changelog)
39
+ - [License](#license)
40
+
41
+ ---
42
+
43
+ ## What is StellaLib?
44
+
45
+ **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.
46
+
47
+ Unlike other Lavalink clients, StellaLib:
48
+
49
+ - **Auto-detects** whether your Lavalink server is v3 or v4 and adapts automatically
50
+ - **Persists sessions** across bot restarts so music keeps playing
51
+ - **Has a smart autoplay engine** that picks the best next track based on listening history
52
+ - **Handles failures gracefully** with reconnect backoff, rate limit retries, and search fallback
53
+
54
+ ## How it Works
55
+
56
+ ```
57
+ ┌──────────────┐ raw voice events ┌──────────────────┐ WebSocket/REST ┌──────────┐
58
+ │ Discord.js │ ──────────────────────► │ StellaManager │ ◄──────────────────► │ Lavalink │
59
+ │ (your bot) │ ◄────── send payloads ── │ │ │ Server │
60
+ └──────────────┘ └──────────────────┘ └──────────┘
61
+
62
+ ┌─────────────┼─────────────┐
63
+ ▼ ▼ ▼
64
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
65
+ │ Node 1 │ │ Node 2 │ │ Node N │
66
+ │ (v4 auto)│ │ (v3 auto)│ │ │
67
+ └──────────┘ └──────────┘ └──────────┘
68
+
69
+ ┌─────┼─────┐
70
+ ▼ ▼ ▼
71
+ ┌────────┐ ┌────────┐
72
+ │Player A│ │Player B│ (one per guild)
73
+ │ Queue │ │ Queue │
74
+ │Filters │ │Filters │
75
+ └────────┘ └────────┘
76
+ ```
77
+
78
+ **Flow:**
79
+
80
+ 1. Your bot receives raw Discord voice events and forwards them to `StellaManager`
81
+ 2. The Manager routes voice data to the correct `Node` (Lavalink server connection)
82
+ 3. Each Node auto-detects its Lavalink version (v3 or v4) and adapts its protocol
83
+ 4. `Player` instances (one per guild) handle playback, queue, volume, and filters
84
+ 5. The Node's `Rest` client handles track loading, player updates, and session management
85
+ 6. Events flow back from Lavalink → Node → Manager → your bot's event handlers
86
+
87
+ ## Architecture
88
+
89
+ StellaLib is composed of several core classes that work together:
90
+
91
+ | Class | What it does |
92
+ |---|---|
93
+ | **`StellaManager`** | The entry point. Manages all nodes and players, handles search, voice state updates, caching, and shutdown. You create one Manager per bot. |
94
+ | **`StellaNode`** | Represents a connection to a single Lavalink server. Handles WebSocket connection, heartbeat, reconnect, version detection, session resume, and autoplay logic. |
95
+ | **`StellaPlayer`** | One player per Discord guild. Controls playback (`play`, `pause`, `stop`, `seek`), manages the queue, applies filters, and handles voice readiness. |
96
+ | **`StellaQueue`** | Extends `Array` with music-specific methods: `add()`, `remove()`, `clear()`, `shuffle()`, repeat modes, and `current`/`previous` track tracking. |
97
+ | **`StellaRest`** | HTTP client for Lavalink's REST API. Version-aware (v3 vs v4 endpoints), with rate limit retry, request deduplication, and timeout handling. |
98
+ | **`StellaFilters`** | Manages audio filters and equalizer settings per player. Built-in presets for common effects. |
99
+ | **`LRUCache`** | Bounded least-recently-used cache with TTL expiry for search results. Reduces redundant API calls. |
100
+ | **`FileSessionStore`** | Persists Lavalink session IDs to a JSON file. Enables seamless resume after bot restarts. |
101
+
102
+ ### Project Structure
103
+
104
+ ```
105
+ src/
106
+ Structures/
107
+ Manager.ts — Main hub: nodes, players, search, voice, cache, shutdown
108
+ Node.ts — Lavalink node: WS, heartbeat, reconnect, version detect, autoplay
109
+ Player.ts — Guild player: playback, queue, voice ready, filters, move node
110
+ Queue.ts — Queue: add/remove/shuffle, repeat modes, current/previous
111
+ Rest.ts — REST client: version-aware endpoints, retry, dedup, timeout
112
+ Filters.ts — Audio filter management and presets
113
+ LRUCache.ts — Bounded LRU cache with TTL and memory estimation
114
+ SessionStore.ts — FileSessionStore for session persistence
115
+ Types.ts — All TypeScript interfaces, types, and event definitions
116
+ Utils.ts — TrackUtils (build/validate tracks), Structure, Plugin system
117
+ Utils/
118
+ FiltersEqualizers.ts — Equalizer band presets for each filter
119
+ ManagerCheck.ts — Manager option validation
120
+ NodeCheck.ts — Node option validation
121
+ PlayerCheck.ts — Player option validation
122
+ index.ts — Re-exports everything
123
+ ```
25
124
 
26
125
  ## Installation
27
126
 
28
127
  ```bash
29
- npm install stellalib
128
+ npm install @stella_project/stellalib
30
129
  # or
31
- yarn add stellalib
130
+ yarn add @stella_project/stellalib
32
131
  # or
33
- bun add stellalib
132
+ bun add @stella_project/stellalib
34
133
  ```
35
134
 
36
135
  ## Quick Start
37
136
 
38
137
  ```ts
39
138
  import { Client, GatewayIntentBits } from "discord.js";
40
- import { StellaManager, FileSessionStore } from "stellalib";
139
+ import { StellaManager, FileSessionStore } from "@stella_project/stellalib";
41
140
 
141
+ // 1. Create your Discord client
42
142
  const client = new Client({
43
143
  intents: [
44
144
  GatewayIntentBits.Guilds,
@@ -48,38 +148,73 @@ const client = new Client({
48
148
  ],
49
149
  });
50
150
 
151
+ // 2. Create the StellaLib manager
51
152
  const manager = new StellaManager({
52
153
  nodes: [
53
154
  {
54
- identifier: "main",
55
- host: "localhost",
56
- port: 2333,
57
- password: "youshallnotpass",
58
- resumeStatus: true,
59
- resumeTimeout: 120,
60
- heartbeatInterval: 30000,
155
+ identifier: "main", // Unique name for this node
156
+ host: "localhost", // Lavalink server host
157
+ port: 2333, // Lavalink server port
158
+ password: "youshallnotpass",// Lavalink password
159
+ resumeStatus: true, // Enable session resuming
160
+ resumeTimeout: 120, // Seconds Lavalink waits for reconnect
161
+ heartbeatInterval: 30000, // Ping interval in ms
61
162
  },
62
163
  ],
63
- autoPlay: true,
64
- defaultSearchPlatform: "spotify",
65
- searchFallback: ["soundcloud", "youtube music", "youtube"],
66
- sessionStore: new FileSessionStore("./sessions.json"),
67
- caches: { enabled: true, time: 60000, maxSize: 200 },
164
+ autoPlay: true, // Enable autoplay when queue ends
165
+ defaultSearchPlatform: "spotify", // Default search source
166
+ searchFallback: ["soundcloud", "youtube"], // Fallback if primary fails
167
+ sessionStore: new FileSessionStore("./sessions.json"), // Persist sessions
168
+ caches: { enabled: true, time: 60000, maxSize: 200 }, // Search cache
68
169
  send(id, payload) {
170
+ // Required: how to send voice payloads to Discord
69
171
  const guild = client.guilds.cache.get(id);
70
172
  if (guild) guild.shard.send(payload);
71
173
  },
72
174
  });
73
175
 
74
- // Forward raw Discord events to StellaLib
176
+ // 3. Forward raw Discord events to StellaLib (required for voice)
75
177
  client.on("raw", (d) => manager.updateVoiceState(d));
76
178
 
179
+ // 4. Initialize manager when bot is ready
77
180
  client.on("ready", () => {
78
181
  console.log(`Bot ready as ${client.user?.tag}`);
79
182
  manager.init(client.user!.id);
80
183
  });
81
184
 
82
- // Graceful shutdown
185
+ // 5. Handle events
186
+ manager.on("NodeConnect", (node) => {
187
+ console.log(`Connected to ${node.options.identifier} (Lavalink v${node.version})`);
188
+ });
189
+
190
+ manager.on("TrackStart", (player, track) => {
191
+ console.log(`Now playing: ${track.title}`);
192
+ });
193
+
194
+ // 6. Play music (example in a command handler)
195
+ async function play(guildId: string, voiceChannelId: string, query: string) {
196
+ // Create or get player
197
+ let player = manager.players.get(guildId);
198
+ if (!player) {
199
+ player = manager.create({
200
+ guild: guildId,
201
+ voiceChannel: voiceChannelId,
202
+ textChannel: "TEXT_CHANNEL_ID",
203
+ volume: 50,
204
+ selfDeafen: true,
205
+ });
206
+ player.connect();
207
+ }
208
+
209
+ // Search and queue
210
+ const res = await manager.search(query, "USER_ID");
211
+ if (res.tracks.length) {
212
+ player.queue.add(res.tracks[0]);
213
+ if (!player.playing) player.play();
214
+ }
215
+ }
216
+
217
+ // 7. Graceful shutdown
83
218
  for (const sig of ["SIGINT", "SIGTERM"]) {
84
219
  process.on(sig, async () => {
85
220
  await manager.shutdown();
@@ -87,184 +222,351 @@ for (const sig of ["SIGINT", "SIGTERM"]) {
87
222
  });
88
223
  }
89
224
 
90
- // Play a track
91
- manager.on("NodeConnect", async () => {
92
- const player = manager.create({
93
- guild: "GUILD_ID",
94
- voiceChannel: "VOICE_CHANNEL_ID",
95
- textChannel: "TEXT_CHANNEL_ID",
96
- });
97
- player.connect();
98
-
99
- const res = await manager.search("never gonna give you up");
100
- if (res.tracks.length) {
101
- player.queue.add(res.tracks[0]);
102
- player.play();
103
- }
104
- });
105
-
106
225
  client.login("YOUR_BOT_TOKEN");
107
226
  ```
108
227
 
109
- ## Session Persistence
228
+ ## Core Concepts
110
229
 
111
- StellaLib can persist Lavalink session IDs across bot restarts, so players keep playing without interruption:
230
+ ### Manager
112
231
 
113
- ```ts
114
- import { FileSessionStore } from "stellalib";
232
+ `StellaManager` is the central hub. You create **one instance** and it manages everything.
115
233
 
234
+ ```ts
116
235
  const manager = new StellaManager({
236
+ nodes: [...], // Array of Lavalink node configs
237
+ send: (id, payload) => { ... }, // How to send to Discord gateway
238
+ autoPlay: true, // Auto-play next track when queue ends
239
+ defaultSearchPlatform: "spotify",
240
+ searchFallback: ["soundcloud", "youtube music"],
117
241
  sessionStore: new FileSessionStore("./sessions.json"),
118
- nodes: [{
119
- resumeStatus: true,
120
- resumeTimeout: 120, // seconds Lavalink waits for reconnect
121
- // ...
122
- }],
123
- // ...
242
+ caches: { enabled: true, time: 60000, maxSize: 200 },
243
+ clientName: "StellaLib",
244
+ shards: 1,
124
245
  });
125
246
 
126
- // On shutdown, sessions are saved automatically
127
- await manager.shutdown();
247
+ // Initialize after Discord client is ready
248
+ manager.init(client.user!.id);
128
249
  ```
129
250
 
130
- You can also implement your own store (e.g., Redis) by implementing the `SessionStore` interface:
251
+ **Key methods:**
252
+ - `manager.init(clientId)` — Connect all nodes
253
+ - `manager.create(options)` — Create a player for a guild
254
+ - `manager.get(guildId)` — Get existing player
255
+ - `manager.search(query, requester?)` — Search tracks with fallback
256
+ - `manager.updateVoiceState(data)` — Forward raw Discord voice events
257
+ - `manager.shutdown()` — Gracefully close everything
258
+ - `manager.getStats()` — Get node/player/cache statistics
259
+
260
+ ### Node
261
+
262
+ `StellaNode` represents a single Lavalink server connection. Nodes are created automatically from the `nodes` config.
263
+
264
+ **What it does automatically:**
265
+ - Detects Lavalink version (v3 or v4) before connecting
266
+ - Establishes WebSocket with the correct URL and headers
267
+ - Sends heartbeat pings to detect dead connections
268
+ - Reconnects with exponential backoff + jitter on disconnect
269
+ - Configures session resuming (v3: WS op, v4: REST PATCH)
270
+ - Syncs player state after resume (v4 only)
271
+ - Handles autoplay logic when queue ends
272
+
273
+ **Properties:**
274
+ - `node.version` — Detected Lavalink version (`3` or `4`)
275
+ - `node.connected` — Whether WebSocket is open
276
+ - `node.stats` — CPU, memory, players, uptime stats
277
+ - `node.info` — Cached Lavalink server info (plugins, sources)
278
+ - `node.penalties` — Calculated penalty score for load balancing
279
+
280
+ ### Player
281
+
282
+ `StellaPlayer` controls playback for **one Discord guild**. Created via `manager.create()`.
131
283
 
132
284
  ```ts
133
- interface SessionStore {
134
- get(nodeId: string): Promise<string | null> | string | null;
135
- set(nodeId: string, sessionId: string): Promise<void> | void;
136
- delete(nodeId: string): Promise<void> | void;
137
- }
285
+ const player = manager.create({
286
+ guild: "GUILD_ID",
287
+ voiceChannel: "VOICE_CHANNEL_ID",
288
+ textChannel: "TEXT_CHANNEL_ID",
289
+ volume: 50,
290
+ selfDeafen: true,
291
+ });
292
+
293
+ player.connect(); // Join voice channel
294
+ player.play(); // Play first track in queue
295
+ player.pause(true); // Pause
296
+ player.pause(false); // Resume
297
+ player.stop(); // Stop current track (plays next)
298
+ player.seek(30000); // Seek to 30 seconds
299
+ player.setVolume(80); // Set volume (0-100)
300
+ player.setTrackRepeat(true); // Repeat current track
301
+ player.setQueueRepeat(true); // Repeat entire queue
302
+ player.setAutoplay(true, botUser); // Enable smart autoplay
303
+ player.moveNode("other-node"); // Move to another Lavalink node
304
+ player.destroy(); // Leave channel and clean up
138
305
  ```
139
306
 
140
- ## Smart Autoplay
307
+ ### Queue
141
308
 
142
- When the queue ends and autoplay is enabled, StellaLib's auto-mix engine finds the best next track:
309
+ `StellaQueue` extends JavaScript's `Array` with music-specific helpers:
143
310
 
144
311
  ```ts
145
- // Enable autoplay for a player
146
- player.setAutoplay(true, { id: user.id, tag: user.tag });
312
+ player.queue.add(track); // Add track(s) to end
313
+ player.queue.add([track1, track2]); // Add multiple
314
+ player.queue.remove(0); // Remove by index
315
+ player.queue.clear(); // Clear all queued tracks
316
+ player.queue.shuffle(); // Randomize order
317
+ player.queue.current; // Currently playing track
318
+ player.queue.previous; // Previously played track
319
+ player.queue.totalSize; // current + queued count
320
+ player.queue.size; // Queued count (excluding current)
147
321
  ```
148
322
 
149
- The engine scores candidates based on:
150
- - **Duration similarity** — Prefer tracks close in length to recent plays
151
- - **Author/title overlap** — Prioritize same artist or related keywords
152
- - **Source consistency** — Stay on the same platform when possible
153
- - **Diversity** — Avoid repeating the same artist too many times
154
- - **History tracking** — Never replay recently heard tracks (last 50)
323
+ ### Rest
155
324
 
156
- Uses multi-seed context from the last 5 tracks for Spotify recommendations, theme keyword extraction, and cross-artist transitions.
325
+ `StellaRest` handles all HTTP communication with Lavalink. It's version-aware the same method call works for both v3 and v4.
157
326
 
158
- ## Search with Fallback
327
+ | Method | v3 behavior | v4 behavior |
328
+ |---|---|---|
329
+ | `loadTracks(id)` | `GET /loadtracks` → normalized | `GET /v4/loadtracks` |
330
+ | `updatePlayer(opts)` | WS ops (`play`, `pause`, etc.) | `PATCH /v4/sessions/.../players/...` |
331
+ | `destroyPlayer(id)` | WS `destroy` op | `DELETE /v4/sessions/.../players/...` |
332
+ | `configureResume(t)` | WS `configureResuming` op | `PATCH /v4/sessions/...` |
333
+ | `getInfo()` | `GET /version` | `GET /v4/info` |
334
+ | `decodeTracks(arr)` | `POST /decodetracks` | `POST /v4/decodetracks` |
159
335
 
160
- ```ts
161
- const manager = new StellaManager({
162
- defaultSearchPlatform: "spotify",
163
- searchFallback: ["soundcloud", "youtube music", "youtube"],
164
- // ...
165
- });
336
+ **Built-in resilience:**
337
+ - Auto-retry on 429 rate limits (up to 3 retries with `Retry-After`)
338
+ - GET request deduplication (concurrent identical GETs share one request)
339
+ - Configurable request timeout
340
+ - Request/failure counters
166
341
 
167
- // If Spotify returns empty, automatically tries SoundCloud, then YouTube Music, then YouTube
168
- const result = await manager.search("natori セレナーデ");
169
- ```
342
+ ### Filters
170
343
 
171
- ## Audio Filters
344
+ Built-in audio filter presets:
172
345
 
173
346
  ```ts
174
- // Toggle filters
175
- await player.filters.setFilter("nightcore", true);
176
347
  await player.filters.setFilter("bassboost", true);
348
+ await player.filters.setFilter("nightcore", true);
177
349
  await player.filters.setFilter("vaporwave", true);
178
350
  await player.filters.setFilter("eightD", true);
351
+ await player.filters.setFilter("slowmo", true);
352
+ await player.filters.setFilter("soft", true);
353
+ await player.filters.setFilter("trebleBass", true);
354
+ await player.filters.setFilter("tv", true);
355
+ await player.filters.setFilter("distort", true);
179
356
 
180
- // Clear all filters
181
- await player.filters.clearFilters();
357
+ await player.filters.clearFilters(); // Remove all
182
358
  ```
183
359
 
184
- Available: `bassboost`, `nightcore`, `vaporwave`, `eightD`, `slowmo`, `soft`, `trebleBass`, `tv`, `distort`
360
+ Each preset applies specific equalizer bands, timescale, rotation, or other Lavalink audio parameters.
185
361
 
186
- ## Manager Stats
362
+ ## Multi-Version Support (v3 + v4)
363
+
364
+ StellaLib **automatically detects** your Lavalink server version before connecting. No configuration needed.
365
+
366
+ **How detection works:**
367
+ 1. Before WebSocket connect, the Node sends `GET /v4/info` to the server
368
+ 2. If it responds `200 OK` → **Lavalink v4** detected (server info is cached)
369
+ 3. If it fails, tries `GET /version` → **Lavalink v3** detected
370
+ 4. Falls back to v4 if both fail
371
+
372
+ **What adapts automatically:**
373
+
374
+ | Aspect | Lavalink v3 | Lavalink v4 |
375
+ |---|---|---|
376
+ | **WebSocket URL** | `ws://host:port` | `ws://host:port/v4/websocket` |
377
+ | **Player control** | WebSocket ops (`play`, `stop`, `pause`, `seek`, `volume`, `filters`) | REST `PATCH` |
378
+ | **Session resume** | `Resume-Key` header + WS `configureResuming` | `Session-Id` header + REST `PATCH` |
379
+ | **Track loading** | `/loadtracks` (response normalized to v4 format) | `/v4/loadtracks` |
380
+ | **Server info** | `/version` (returns version string) | `/v4/info` (returns full info JSON) |
381
+ | **Player sync** | Not available (v3 limitation) | Full player state sync on resume |
382
+ | **Track data** | `track` field → mapped to `encoded` | `encoded` field |
383
+ | **Load types** | `TRACK_LOADED` → `track`, `SEARCH_RESULT` → `search`, etc. | Already v4 format |
187
384
 
188
385
  ```ts
189
- const stats = manager.getStats();
190
- // { nodes, players, playingPlayers, cacheSize, cacheMemoryEstimate }
386
+ manager.on("NodeConnect", (node) => {
387
+ console.log(`Lavalink v${node.version}`); // 3 or 4
388
+ });
191
389
  ```
192
390
 
193
- ## Events
391
+ ## Session Persistence
392
+
393
+ StellaLib persists session IDs so music **keeps playing** after bot restarts.
194
394
 
195
395
  ```ts
196
- manager.on("NodeConnect", (node) => { });
197
- manager.on("NodeDisconnect", (node, reason) => { });
198
- manager.on("NodeError", (node, error) => { });
199
- manager.on("NodeReconnect", (node) => { });
200
- manager.on("NodeRaw", (payload) => { });
201
- manager.on("TrackStart", (player, track, payload) => { });
202
- manager.on("TrackEnd", (player, track, payload) => { });
203
- manager.on("TrackStuck", (player, track, payload) => { });
204
- manager.on("TrackError", (player, track, payload) => { });
205
- manager.on("QueueEnd", (player, track, payload) => { });
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) => { });
396
+ import { FileSessionStore } from "@stella_project/stellalib";
397
+
398
+ const manager = new StellaManager({
399
+ sessionStore: new FileSessionStore("./sessions.json"),
400
+ nodes: [{
401
+ resumeStatus: true, // Tell Lavalink to hold the session
402
+ resumeTimeout: 120, // Seconds to wait before destroying session
403
+ // ...
404
+ }],
405
+ // ...
406
+ });
213
407
  ```
214
408
 
215
- ## Project Structure
409
+ **How it works:**
410
+ 1. On connect, Node loads saved session ID from the store
411
+ 2. Sends it as `Session-Id` (v4) or `Resume-Key` (v3) header
412
+ 3. Lavalink resumes the session — players keep their state
413
+ 4. On disconnect/shutdown, session ID is persisted to the store
414
+
415
+ **Custom stores** (e.g., Redis, database):
216
416
 
417
+ ```ts
418
+ const manager = new StellaManager({
419
+ sessionStore: {
420
+ async get(nodeId) { return await redis.get(`session:${nodeId}`); },
421
+ async set(nodeId, sessionId) { await redis.set(`session:${nodeId}`, sessionId); },
422
+ async delete(nodeId) { await redis.del(`session:${nodeId}`); },
423
+ },
424
+ // ...
425
+ });
217
426
  ```
218
- src/
219
- Structures/
220
- Manager.ts — Main hub: manages nodes, players, search, voice updates
221
- Node.ts — Lavalink node: WebSocket, reconnect, session resume, autoplay
222
- Player.ts — Guild player: playback, queue, voice readiness, filters
223
- Queue.ts — Queue: extends Array with add/remove/shuffle
224
- Rest.ts — REST client with retry, timeout, and deduplication
225
- Filters.ts — Audio filters and presets
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
427
+
428
+ ## Smart Autoplay
429
+
430
+ When the queue ends and autoplay is enabled, StellaLib's auto-mix engine picks the best next track.
431
+
432
+ ```ts
433
+ player.setAutoplay(true, client.user);
236
434
  ```
237
435
 
238
- ## Multi-Version Support
436
+ **How the engine works:**
239
437
 
240
- StellaLib auto-detects your Lavalink server version on connect and adapts:
438
+ 1. **Seed collection** Gathers the last 5 played tracks as context seeds
439
+ 2. **Source detection** — Identifies if the listener was on Spotify, YouTube, or SoundCloud
440
+ 3. **Recommendation fetch** — Uses Spotify `sprec:` (seed artists + seed tracks) or YouTube Mix
441
+ 4. **Candidate scoring** — Each candidate is scored on:
442
+ - Duration similarity to recent tracks
443
+ - Author/title keyword overlap
444
+ - Remix/cover penalty (avoids non-originals)
445
+ - History check (never replays last 50 tracks)
446
+ 5. **Best transition** — Picks the highest-scoring candidate
447
+ 6. **Cross-platform mirror** — If needed, re-searches on SoundCloud/YouTube for a streamable version
448
+ 7. **Fallback chain** — If recommendations fail, tries theme-based search, then random from same artist
241
449
 
242
- | Feature | Lavalink v3 | Lavalink v4 |
243
- |---|---|---|
244
- | **WebSocket URL** | `ws://host:port` | `ws://host:port/v4/websocket` |
245
- | **Player ops** | WebSocket ops (`play`, `stop`, `pause`, etc.) | REST PATCH |
246
- | **Session resume** | `Resume-Key` header + WS `configureResuming` | `Session-Id` header + REST PATCH |
247
- | **Load tracks** | `/loadtracks` (normalized to v4 format) | `/v4/loadtracks` |
248
- | **Server info** | `/version` | `/v4/info` |
249
- | **Player sync** | Not available | Full player state sync |
450
+ ## Search with Fallback
250
451
 
251
452
  ```ts
252
- // No configuration needed — version is auto-detected
253
453
  const manager = new StellaManager({
254
- nodes: [{ host: "my-v3-server.com", port: 2333 }],
454
+ defaultSearchPlatform: "spotify",
455
+ searchFallback: ["soundcloud", "youtube music", "youtube"],
255
456
  // ...
256
457
  });
257
458
 
258
- // Check detected version after connect
259
- manager.on("NodeConnect", (node) => {
260
- console.log(`Connected to Lavalink v${node.version}`); // 3 or 4
261
- });
459
+ // Searches Spotify first. If empty, tries SoundCloud, then YouTube Music, then YouTube.
460
+ const result = await manager.search("natori セレナーデ", userId);
461
+ ```
462
+
463
+ **Supported platforms:** `spotify`, `soundcloud`, `youtube`, `youtube music`, `deezer`, `tidal`, `applemusic`, `bandcamp`, `jiosaavn`
464
+
465
+ ## Audio Filters
466
+
467
+ | Filter | Effect |
468
+ |---|---|
469
+ | `bassboost` | Boosts low frequencies |
470
+ | `nightcore` | Speeds up + higher pitch |
471
+ | `vaporwave` | Slows down + lower pitch |
472
+ | `eightD` | Rotating stereo panning |
473
+ | `slowmo` | Slower playback speed |
474
+ | `soft` | Reduces harsh frequencies |
475
+ | `trebleBass` | Boosts both high and low bands |
476
+ | `tv` | Tinny speaker simulation |
477
+ | `distort` | Audio distortion effect |
478
+
479
+ ## Events Reference
480
+
481
+ | Event | Parameters | Description |
482
+ |---|---|---|
483
+ | `NodeCreate` | `(node)` | Node instance created |
484
+ | `NodeConnect` | `(node)` | WebSocket connection established |
485
+ | `NodeReconnect` | `(node)` | Attempting reconnection |
486
+ | `NodeDisconnect` | `(node, reason)` | WebSocket disconnected |
487
+ | `NodeDestroy` | `(node)` | Node destroyed |
488
+ | `NodeError` | `(node, error)` | Error on node |
489
+ | `NodeRaw` | `(payload)` | Raw WebSocket message |
490
+ | `TrackStart` | `(player, track, payload)` | Track started playing |
491
+ | `TrackEnd` | `(player, track, payload)` | Track finished |
492
+ | `TrackStuck` | `(player, track, payload)` | Track got stuck |
493
+ | `TrackError` | `(player, track, payload)` | Track playback error |
494
+ | `QueueEnd` | `(player, track, payload)` | Queue finished (all tracks played) |
495
+ | `PlayerCreate` | `(player)` | Player created for a guild |
496
+ | `PlayerDestroy` | `(player)` | Player destroyed |
497
+ | `PlayerMove` | `(player, oldChannel, newChannel)` | Bot moved to different voice channel |
498
+ | `PlayerDisconnect` | `(player, oldChannel)` | Bot disconnected from voice |
499
+ | `PlayerStateUpdate` | `(oldPlayer, newPlayer)` | Player state changed |
500
+ | `SocketClosed` | `(player, payload)` | Lavalink WebSocket closed for player |
501
+ | `Debug` | `(message)` | Debug log message |
502
+
503
+ ## Configuration Reference
504
+
505
+ ### Manager Options
506
+
507
+ ```ts
508
+ interface ManagerOptions {
509
+ nodes: NodeOptions[]; // Lavalink server configs (required)
510
+ send: (id: string, payload: Payload) => void; // Discord gateway send (required)
511
+ clientId?: string; // Bot user ID (set by init())
512
+ clientName?: string; // Client identifier sent to Lavalink
513
+ shards?: number; // Shard count
514
+ autoPlay?: boolean; // Enable autoplay on queue end
515
+ defaultSearchPlatform?: SearchPlatform;// Default search source
516
+ searchFallback?: string[]; // Fallback platforms
517
+ sessionStore?: SessionStore; // Session persistence store
518
+ caches?: {
519
+ enabled: boolean;
520
+ time: number; // TTL in ms
521
+ maxSize: number; // Max cached entries
522
+ };
523
+ plugins?: Plugin[]; // Custom plugins
524
+ }
525
+ ```
526
+
527
+ ### Node Options
528
+
529
+ ```ts
530
+ interface NodeOptions {
531
+ host: string; // Lavalink host
532
+ port: number; // Lavalink port
533
+ password: string; // Lavalink password
534
+ identifier?: string; // Unique node name
535
+ secure?: boolean; // Use wss:// and https://
536
+ retryAmount?: number; // Max reconnect attempts
537
+ retryDelay?: number; // Base delay between retries (ms)
538
+ requestTimeout?: number; // REST request timeout (ms)
539
+ resumeStatus?: boolean; // Enable session resuming
540
+ resumeTimeout?: number; // Seconds Lavalink holds session
541
+ heartbeatInterval?: number;// WebSocket ping interval (ms)
542
+ }
262
543
  ```
263
544
 
264
545
  ## Requirements
265
546
 
266
547
  - **Node.js** >= 18.0.0
267
548
  - **Lavalink** v3.x or v4.x
549
+ - **Discord.js** v14+ (or any library that exposes raw gateway events)
550
+
551
+ ## Documentation
552
+
553
+ For detailed guides and API reference, see the [docs/](docs/) folder:
554
+
555
+ - [Getting Started](docs/01-getting-started.md)
556
+ - [Architecture](docs/02-architecture.md)
557
+ - [Manager](docs/03-manager.md)
558
+ - [Node](docs/04-node.md)
559
+ - [Player](docs/05-player.md)
560
+ - [Queue](docs/06-queue.md)
561
+ - [Events](docs/07-events.md)
562
+ - [Filters](docs/08-filters.md)
563
+ - [Session Persistence](docs/09-session-persistence.md)
564
+ - [Multi-Version Support](docs/10-multi-version.md)
565
+ - [Autoplay Engine](docs/11-autoplay.md)
566
+
567
+ ## Changelog
568
+
569
+ See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes per version.
268
570
 
269
571
  ## License
270
572
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stella_project/stellalib",
3
- "version": "1.0.1",
3
+ "version": "1.0.21",
4
4
  "description": "StellaLib — A powerful Lavalink v3+v4 client for TypeScript with auto version detection, session persistence, smart autoplay, and graceful shutdown",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",