ziplayer 0.2.7-dev.3 → 0.3.0
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/AI-Guide.md +624 -607
- package/README.md +526 -524
- package/dist/plugins/index.d.ts +62 -12
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +497 -57
- package/dist/plugins/index.js.map +1 -1
- package/dist/structures/PersistenceManager.d.ts +96 -0
- package/dist/structures/PersistenceManager.d.ts.map +1 -0
- package/dist/structures/PersistenceManager.js +1008 -0
- package/dist/structures/PersistenceManager.js.map +1 -0
- package/dist/structures/Player.d.ts +109 -18
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +902 -182
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +1 -22
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +1 -73
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/StreamManager.d.ts +137 -0
- package/dist/structures/StreamManager.d.ts.map +1 -0
- package/dist/structures/StreamManager.js +420 -0
- package/dist/structures/StreamManager.js.map +1 -0
- package/dist/types/index.d.ts +149 -16
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/persistence.d.ts +3 -2
- package/dist/types/persistence.d.ts.map +1 -1
- package/package.json +47 -47
- package/src/extensions/BaseExtension.ts +36 -36
- package/src/extensions/index.ts +473 -473
- package/src/index.ts +16 -16
- package/src/plugins/BasePlugin.ts +27 -27
- package/src/plugins/index.ts +950 -403
- package/src/structures/FilterManager.ts +303 -303
- package/src/structures/Player.ts +2797 -1970
- package/src/structures/PlayerManager.ts +725 -822
- package/src/structures/Queue.ts +599 -599
- package/src/structures/StreamManager.ts +524 -0
- package/src/types/extension.ts +129 -129
- package/src/types/fillter.ts +264 -264
- package/src/types/index.ts +548 -415
- package/src/types/plugin.ts +59 -59
- package/src/utils/timeout.ts +10 -10
- package/tsconfig.json +22 -22
- package/src/persistence/PersistenceManager.ts +0 -1077
- package/src/types/persistence.ts +0 -85
package/README.md
CHANGED
|
@@ -1,524 +1,526 @@
|
|
|
1
|
-
<img width="1175" height="305" alt="logo" src="https://raw.githubusercontent.com/ZiProject/ZiPlayer/refs/heads/main/publish/logo.png" />
|
|
2
|
-
|
|
3
|
-
# ZiPlayer
|
|
4
|
-
|
|
5
|
-
A powerful, extensible Discord music engine built on top of `@discordjs/voice`, designed for scalability, flexibility, and
|
|
6
|
-
developer experience.
|
|
7
|
-
|
|
8
|
-
ZiPlayer is not just a player — it's a **full ecosystem** with plugins, extensions, and a modular architecture that lets you build
|
|
9
|
-
advanced music bots quickly.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## ✨ Highlights
|
|
14
|
-
|
|
15
|
-
- 🔌 **Plugin-driven architecture** — Easily support new audio sources
|
|
16
|
-
- 🌐 **Multi-source playback** — YouTube, SoundCloud, Spotify (with fallback), TTS, and more
|
|
17
|
-
- 🧠 **Smart fallback system** — Automatically resolves streams across plugins
|
|
18
|
-
- 🎛️ **Advanced audio filters** — Real-time FFmpeg effects (bassboost, nightcore, etc.)
|
|
19
|
-
- 🔁 **Autoplay & looping** — Seamless listening experience
|
|
20
|
-
- 🧩 **Extension system** — Add STT, lyrics, Lavalink, and custom logic
|
|
21
|
-
- 🗂️ **Per-guild player system** — Scales across multiple Discord servers
|
|
22
|
-
- 📡 **Event-driven core** — Full lifecycle hooks for customization
|
|
23
|
-
- 💾 **Custom userdata** — Attach context to each player
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
player.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
player.
|
|
125
|
-
player.
|
|
126
|
-
player.
|
|
127
|
-
player.
|
|
128
|
-
player.
|
|
129
|
-
player.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
player.queue.
|
|
145
|
-
player.queue.
|
|
146
|
-
player.queue.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
player.queue.
|
|
151
|
-
player.queue.
|
|
152
|
-
player.queue.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
player.queue.
|
|
156
|
-
player.queue.
|
|
157
|
-
player.queue.
|
|
158
|
-
player.queue.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
player.queue.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
- **
|
|
227
|
-
- **
|
|
228
|
-
- **
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
```ts
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
1
|
+
<img width="1175" height="305" alt="logo" src="https://raw.githubusercontent.com/ZiProject/ZiPlayer/refs/heads/main/publish/logo.png" />
|
|
2
|
+
|
|
3
|
+
# ZiPlayer
|
|
4
|
+
|
|
5
|
+
A powerful, extensible Discord music engine built on top of `@discordjs/voice`, designed for scalability, flexibility, and
|
|
6
|
+
developer experience.
|
|
7
|
+
|
|
8
|
+
ZiPlayer is not just a player — it's a **full ecosystem** with plugins, extensions, and a modular architecture that lets you build
|
|
9
|
+
advanced music bots quickly.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Highlights
|
|
14
|
+
|
|
15
|
+
- 🔌 **Plugin-driven architecture** — Easily support new audio sources
|
|
16
|
+
- 🌐 **Multi-source playback** — YouTube, SoundCloud, Spotify (with fallback), TTS, and more
|
|
17
|
+
- 🧠 **Smart fallback system** — Automatically resolves streams across plugins
|
|
18
|
+
- 🎛️ **Advanced audio filters** — Real-time FFmpeg effects (bassboost, nightcore, etc.)
|
|
19
|
+
- 🔁 **Autoplay & looping** — Seamless listening experience
|
|
20
|
+
- 🧩 **Extension system** — Add STT, lyrics, Lavalink, and custom logic
|
|
21
|
+
- 🗂️ **Per-guild player system** — Scales across multiple Discord servers
|
|
22
|
+
- 📡 **Event-driven core** — Full lifecycle hooks for customization
|
|
23
|
+
- 💾 **Custom userdata** — Attach context to each player
|
|
24
|
+
- ⚡ **Smart caching** — Search and stream caching for better performance
|
|
25
|
+
- 🎯 **Queue management** — Advanced queue operations (move, swap, batch remove)
|
|
26
|
+
- 💹 **Preload** - Auto Preload next Track
|
|
27
|
+
- 🔃 **Crossfade** - Suport crossfade for new/slip Track
|
|
28
|
+
- 🧠 **Transition Engine** - BPM/genre-aware crossfade (chill → long fade, EDM → short fade) with beat-aligned entry instead of
|
|
29
|
+
blind time-based fading
|
|
30
|
+
- 🔄 **Anti-Stuck Recovery 2.0** - Automatic stream failure recovery: reuse preload → fallback plugin → reduce quality →
|
|
31
|
+
controlled skip (no chaotic skipping)
|
|
32
|
+
- 🔊 **Loudness Normalization** - LUFS-based normalization prevents sudden volume jumps between tracks, with gentle limiter to
|
|
33
|
+
avoid distortion
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 📦 Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install ziplayer @ziplayer/plugin @ziplayer/extension @ziplayer/infinity @discordjs/voice discord.js opusscript
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 🚀 Quick Start
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { Client, GatewayIntentBits } from "discord.js";
|
|
49
|
+
import { PlayerManager } from "ziplayer";
|
|
50
|
+
import { YouTubePlugin, SoundCloudPlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
51
|
+
import { InfinityPlugin } from "@ziplayer/infinity";
|
|
52
|
+
|
|
53
|
+
const client = new Client({
|
|
54
|
+
intents: [
|
|
55
|
+
GatewayIntentBits.Guilds,
|
|
56
|
+
GatewayIntentBits.GuildVoiceStates,
|
|
57
|
+
GatewayIntentBits.GuildMessages,
|
|
58
|
+
GatewayIntentBits.MessageContent,
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const manager = new PlayerManager({
|
|
63
|
+
plugins: [new YouTubePlugin(), new SoundCloudPlugin(), new SpotifyPlugin(), new InfinityPlugin()],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
client.on("messageCreate", async (msg) => {
|
|
67
|
+
if (!msg.content.startsWith("!play ") || !msg.guildId) return;
|
|
68
|
+
|
|
69
|
+
const voiceChannel = msg.member?.voice?.channel;
|
|
70
|
+
if (!voiceChannel) return msg.reply("Join a voice channel first!");
|
|
71
|
+
|
|
72
|
+
const player = await manager.create(msg.guildId, {
|
|
73
|
+
leaveOnEnd: true,
|
|
74
|
+
userdata: { channel: msg.channel },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!player.connection) await player.connect(voiceChannel);
|
|
78
|
+
await player.play(msg.content.slice(6), msg.author.id);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🧱 Architecture Overview
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
PlayerManager (global)
|
|
90
|
+
└── Player (per guild)
|
|
91
|
+
├── Queue (advanced operations)
|
|
92
|
+
├── PluginManager (with caching & fallback)
|
|
93
|
+
├── ExtensionManager (with priority & caching)
|
|
94
|
+
└── FilterManager (FFmpeg filters)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Flow
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
create → connect → play → stream → events → destroy
|
|
101
|
+
↓
|
|
102
|
+
auto-save (periodic)
|
|
103
|
+
↓
|
|
104
|
+
restore on restart
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 🎵 Core Usage
|
|
110
|
+
|
|
111
|
+
### Play music
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
await player.play("Never Gonna Give You Up", userId);
|
|
115
|
+
await player.play("https://youtube.com/watch?v=...", userId);
|
|
116
|
+
await player.play("tts: Hello world", userId);
|
|
117
|
+
await player.play(searchResult, userId); // Play from SearchResult
|
|
118
|
+
await player.play(null); // Resume from queue
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Controls
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
player.pause();
|
|
125
|
+
player.resume();
|
|
126
|
+
player.skip();
|
|
127
|
+
player.skip(2); // Skip to track at index 2
|
|
128
|
+
player.stop();
|
|
129
|
+
player.setVolume(100);
|
|
130
|
+
player.loop("track"); // Loop current track
|
|
131
|
+
player.loop("queue"); // Loop entire queue
|
|
132
|
+
player.loop(1); // Number mode: 0=off, 1=track, 2=queue
|
|
133
|
+
player.shuffle();
|
|
134
|
+
player.seek(30000); // Seek to 30 seconds
|
|
135
|
+
player.previous(); // Go back to previous track
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Queue Management
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// Basic operations
|
|
142
|
+
player.queue.add(track);
|
|
143
|
+
player.queue.addMultiple([track1, track2]);
|
|
144
|
+
player.queue.remove(0);
|
|
145
|
+
player.queue.removeMultiple([0, 2, 5]); // Remove multiple indices
|
|
146
|
+
player.queue.removeWhere((t) => t.source === "youtube"); // Remove by condition
|
|
147
|
+
player.queue.clear();
|
|
148
|
+
|
|
149
|
+
// Queue manipulation
|
|
150
|
+
player.queue.move(3, 0); // Move track at index 3 to front
|
|
151
|
+
player.queue.swap(1, 3); // Swap positions 1 and 3
|
|
152
|
+
player.queue.shuffle();
|
|
153
|
+
|
|
154
|
+
// Queue inspection
|
|
155
|
+
player.queue.size;
|
|
156
|
+
player.queue.isEmpty;
|
|
157
|
+
player.queue.currentTrack;
|
|
158
|
+
player.queue.nextTrack;
|
|
159
|
+
player.queue.lastTrack;
|
|
160
|
+
player.queue.previousTracks;
|
|
161
|
+
player.queue.getTrack(5);
|
|
162
|
+
player.queue.findTracks((t) => t.duration > 300000);
|
|
163
|
+
player.queue.indexOf(track);
|
|
164
|
+
player.queue.has(track);
|
|
165
|
+
|
|
166
|
+
// History navigation
|
|
167
|
+
player.queue.jumpToHistory(2); // Go back 2 tracks
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 🔌 Plugins
|
|
173
|
+
|
|
174
|
+
Install via `@ziplayer/plugin`:
|
|
175
|
+
|
|
176
|
+
- **YouTubePlugin** — YouTube + search
|
|
177
|
+
- **SoundCloudPlugin** — SoundCloud streaming
|
|
178
|
+
- **SpotifyPlugin** — Metadata (uses fallback)
|
|
179
|
+
- **TTSPlugin** — Text-to-speech
|
|
180
|
+
- **AttachmentsPlugin** — Local/URL audio files
|
|
181
|
+
|
|
182
|
+
### Example
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { TTSPlugin } from "@ziplayer/plugin";
|
|
186
|
+
|
|
187
|
+
new PlayerManager({
|
|
188
|
+
plugins: [new TTSPlugin({ defaultLang: "en" })],
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Dynamic Plugin Registration
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// Register plugin after initialization
|
|
196
|
+
manager.registerPlugin(new YouTubePlugin());
|
|
197
|
+
|
|
198
|
+
// Get all registered plugins
|
|
199
|
+
const plugins = manager.getPlugins();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 🧩 Extensions
|
|
205
|
+
|
|
206
|
+
Enhance player behavior:
|
|
207
|
+
|
|
208
|
+
- 🎤 `voiceExt` — Speech-to-text commands
|
|
209
|
+
- 🎤 `lyricsExt` — Auto lyrics (synced support)
|
|
210
|
+
- ⚡ `lavalinkExt` — External Lavalink node
|
|
211
|
+
|
|
212
|
+
### Example
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { voiceExt, lyricsExt } from "@ziplayer/extension";
|
|
216
|
+
|
|
217
|
+
const manager = new PlayerManager({
|
|
218
|
+
extensions: [new voiceExt(null, { lang: "en-US" }), new lyricsExt(null, { provider: "lrclib" })],
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Extension Capabilities
|
|
223
|
+
|
|
224
|
+
Extensions can now provide:
|
|
225
|
+
|
|
226
|
+
- **Search** — Custom search handling
|
|
227
|
+
- **Stream** — Custom stream sources (Lavalink, etc.)
|
|
228
|
+
- **Before/After play hooks** — Modify playback behavior
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 🎛️ Audio Filters
|
|
233
|
+
|
|
234
|
+
Apply FFmpeg filters in real-time:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
await player.filter.applyFilter("bassboost");
|
|
238
|
+
await player.filter.applyFilter("nightcore");
|
|
239
|
+
await player.filter.applyFilters(["bassboost", "trebleboost"]); // Multiple filters
|
|
240
|
+
await player.filter.getFilterString(); // "bassboost,trebleboost"
|
|
241
|
+
await player.filter.clearAll();
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Available filters
|
|
245
|
+
|
|
246
|
+
- bassboost, trebleboost
|
|
247
|
+
- nightcore, lofi, vaporwave
|
|
248
|
+
- echo, reverb, chorus
|
|
249
|
+
- karaoke
|
|
250
|
+
- normalize, compressor, limiter
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 🔊 TTS (Interrupt Mode)
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const player = await manager.create(guildId, {
|
|
258
|
+
tts: {
|
|
259
|
+
createPlayer: true,
|
|
260
|
+
interrupt: true,
|
|
261
|
+
volume: 100,
|
|
262
|
+
maxTimeTts: 60000,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await player.play("tts: Hello everyone", userId);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 📡 Events
|
|
272
|
+
|
|
273
|
+
Listen globally via manager:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
manager.on("trackStart", (player, track) => {});
|
|
277
|
+
manager.on("trackEnd", (player, track) => {});
|
|
278
|
+
manager.on("queueEnd", (player) => {});
|
|
279
|
+
manager.on("playerError", (player, error, track) => {});
|
|
280
|
+
manager.on("playerPause", (player, track) => {});
|
|
281
|
+
manager.on("playerResume", (player, track) => {});
|
|
282
|
+
manager.on("volumeChange", (player, oldVolume, newVolume) => {});
|
|
283
|
+
manager.on("queueAdd", (player, track) => {});
|
|
284
|
+
manager.on("queueAddList", (player, tracks) => {});
|
|
285
|
+
manager.on("queueRemove", (player, track, index) => {});
|
|
286
|
+
manager.on("playerDestroy", (player) => {});
|
|
287
|
+
manager.on("ttsStart", (player, payload) => {});
|
|
288
|
+
manager.on("ttsEnd", (player) => {});
|
|
289
|
+
manager.on("stats", (PlayerStats) => {});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 🧠 Advanced Features
|
|
295
|
+
|
|
296
|
+
### Autoplay
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
player.queue.autoPlay(true);
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Insert next track
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
await player.insert("song", 0); // Insert at position 0 (play next)
|
|
306
|
+
await player.insert([track1, track2], 2); // Insert multiple at index 2
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Save stream to file
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
const stream = await player.save(track);
|
|
313
|
+
stream.pipe(fs.createWriteStream("song.mp3"));
|
|
314
|
+
|
|
315
|
+
// Save with filters
|
|
316
|
+
const filteredStream = await player.save(track, {
|
|
317
|
+
filter: ["bassboost"],
|
|
318
|
+
seek: 30000, // Start from 30 seconds
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Progress Bar
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
// Default (compact time format)
|
|
326
|
+
console.log(player.getProgressBar());
|
|
327
|
+
// Output: "1:22:12 ▬▬▬▬▬▬▬▬▬▬🔘▬▬▬▬▬▬▬▬ 1:45:30"
|
|
328
|
+
|
|
329
|
+
// Custom options
|
|
330
|
+
console.log(
|
|
331
|
+
player.getProgressBar({
|
|
332
|
+
size: 30,
|
|
333
|
+
barChar: "─",
|
|
334
|
+
progressChar: "●",
|
|
335
|
+
timeFormat: "full", // "full" or "compact"
|
|
336
|
+
showPercentage: true,
|
|
337
|
+
}),
|
|
338
|
+
);
|
|
339
|
+
// Output: "01:22:12 ───────●───────────────────── 01:45:30 (47%)"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Time Formatting
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
const time = player.getTime();
|
|
346
|
+
console.log(time.formatted.current); // "1:22:12" (compact)
|
|
347
|
+
console.log(time.format); // "01:22:12" (full with leading zeros)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Batch Operations
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
// Broadcast action to all players
|
|
354
|
+
manager.broadcast("setVolume", 50);
|
|
355
|
+
manager.broadcast("pause");
|
|
356
|
+
|
|
357
|
+
// Get players by filter
|
|
358
|
+
const activePlayers = manager.getPlayersByFilter((p) => p.isPlaying);
|
|
359
|
+
|
|
360
|
+
// Delete multiple players
|
|
361
|
+
manager.deleteWhere((p) => p.queue.isEmpty && !p.isPlaying);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## ⚙️ Advanced Configuration
|
|
367
|
+
|
|
368
|
+
### PlayerManager Options
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
const manager = new PlayerManager({
|
|
372
|
+
plugins: [...],
|
|
373
|
+
extensions: [...],
|
|
374
|
+
extractorTimeout: 30000, // Timeout for stream extraction
|
|
375
|
+
autoCleanup: true, // Auto cleanup inactive players
|
|
376
|
+
cleanupInterval: 120000, // Cleanup interval (ms)
|
|
377
|
+
enableSearchCache: true, // Cache search results
|
|
378
|
+
enableStatsCollection: true, // Enable stats events
|
|
379
|
+
persistence: {...} // Persistence configuration
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Player Options
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
const player = await manager.create(guildId, {
|
|
387
|
+
volume: 100,
|
|
388
|
+
quality: "high",
|
|
389
|
+
leaveOnEnd: true,
|
|
390
|
+
leaveOnEmpty: true,
|
|
391
|
+
leaveTimeout: 100000,
|
|
392
|
+
selfDeaf: true,
|
|
393
|
+
selfMute: false,
|
|
394
|
+
extractorTimeout: 50000,
|
|
395
|
+
filters: ["bassboost", "nightcore"],
|
|
396
|
+
tts: {
|
|
397
|
+
createPlayer: false,
|
|
398
|
+
interrupt: true,
|
|
399
|
+
volume: 100,
|
|
400
|
+
maxTimeTts: 60000,
|
|
401
|
+
},
|
|
402
|
+
// Runtime profile
|
|
403
|
+
lowPerformance: false,
|
|
404
|
+
preload: {
|
|
405
|
+
enabled: true,
|
|
406
|
+
autoDisableInLowPerformance: true,
|
|
407
|
+
},
|
|
408
|
+
crossfade: {
|
|
409
|
+
enabled: undefined, // omit to let autoEnable decide
|
|
410
|
+
autoEnable: true,
|
|
411
|
+
autoDisableInLowPerformance: true,
|
|
412
|
+
durationMs: 5000,
|
|
413
|
+
},
|
|
414
|
+
smartTransition: {
|
|
415
|
+
enabled: true,
|
|
416
|
+
genreAware: true,
|
|
417
|
+
beatAlign: true,
|
|
418
|
+
baseDurationMs: 5000,
|
|
419
|
+
minDurationMs: 1200,
|
|
420
|
+
maxDurationMs: 8000,
|
|
421
|
+
genreDurations: { chill: 7000, edm: 2200 },
|
|
422
|
+
beatAlignMaxWaitMs: 1200,
|
|
423
|
+
},
|
|
424
|
+
antiStuck: {
|
|
425
|
+
enabled: true,
|
|
426
|
+
maxRetries: 2,
|
|
427
|
+
retryDelayMs: 900,
|
|
428
|
+
reusePreloadFirst: true,
|
|
429
|
+
reduceQualityOnRetry: true,
|
|
430
|
+
controlledSkipThreshold: 3,
|
|
431
|
+
},
|
|
432
|
+
loudnessNormalization: {
|
|
433
|
+
enabled: true,
|
|
434
|
+
targetLUFS: -14,
|
|
435
|
+
maxBoostDb: 8,
|
|
436
|
+
maxCutDb: 10,
|
|
437
|
+
limiterCeiling: 0.95,
|
|
438
|
+
},
|
|
439
|
+
userdata: { customField: "value" },
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Crossfade + Low Performance
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
// Auto mode: crossfade/preload enabled unless lowPerformance is on
|
|
447
|
+
const player = await manager.create(guildId, {
|
|
448
|
+
lowPerformance: false,
|
|
449
|
+
preload: { enabled: true, autoDisableInLowPerformance: true },
|
|
450
|
+
crossfade: { autoEnable: true, autoDisableInLowPerformance: true, durationMs: 4000 },
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Low performance mode: auto disable preload and crossfade
|
|
454
|
+
const litePlayer = await manager.create(guildId, {
|
|
455
|
+
lowPerformance: true,
|
|
456
|
+
preload: { enabled: true, autoDisableInLowPerformance: true }, // resolved: disabled
|
|
457
|
+
crossfade: { autoEnable: true, autoDisableInLowPerformance: true }, // resolved: disabled
|
|
458
|
+
});
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
> Crossfade is applied when switching to the next track and when calling `player.skip()`. Smart transition adapts fade by
|
|
462
|
+
> `metadata.genre` and can align to beat using `metadata.bpm`. Loudness normalization uses `metadata.lufs` when available and
|
|
463
|
+
> applies a limiter ceiling.
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## 📊 Monitoring & Stats
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
// Get manager statistics
|
|
471
|
+
const stats = manager.getStats();
|
|
472
|
+
console.log({
|
|
473
|
+
totalPlayers: stats.totalPlayers,
|
|
474
|
+
activePlayers: stats.activePlayers,
|
|
475
|
+
pausedPlayers: stats.pausedPlayers,
|
|
476
|
+
connectedPlayers: stats.connectedPlayers,
|
|
477
|
+
totalTracksInQueue: stats.totalTracksInQueue,
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Get plugin/extension stats
|
|
481
|
+
console.log(manager.getConfig());
|
|
482
|
+
console.log(player.pluginManager.getStats());
|
|
483
|
+
console.log(player.extensionManager.getStats());
|
|
484
|
+
|
|
485
|
+
// Clear caches
|
|
486
|
+
player.clearSearchCache();
|
|
487
|
+
player.extensionManager.clearCache("search");
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## ⚠️ Best Practices
|
|
493
|
+
|
|
494
|
+
- Use **one PlayerManager** per bot
|
|
495
|
+
- Always `await player.connect()` before playing
|
|
496
|
+
- Handle `playerError` events
|
|
497
|
+
- Do not reuse a destroyed player
|
|
498
|
+
- Enable **persistence** for production bots to survive restarts
|
|
499
|
+
- Use **autoCleanup** to prevent memory leaks
|
|
500
|
+
- Set appropriate **extractorTimeout** based on your network (default: 10-50 seconds)
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
## 🌟 Migration Guide
|
|
505
|
+
|
|
506
|
+
### From v1.x to v2.x
|
|
507
|
+
|
|
508
|
+
- `player.getTime()` now returns `{ current, total, format, formatted }`
|
|
509
|
+
- `player.getProgressBar()` supports new options
|
|
510
|
+
- `player.queue.remove(index)` removed track is now returned
|
|
511
|
+
- New `queue.removeMultiple()`, `queue.move()`, `queue.swap()` methods
|
|
512
|
+
- Extension hooks now support async properly
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 📚 Resources
|
|
517
|
+
|
|
518
|
+
- Examples: [https://github.com/ZiProject/ZiPlayer/tree/main/examples](https://github.com/ZiProject/ZiPlayer/tree/main/examples)
|
|
519
|
+
- GitHub: [https://github.com/ZiProject/ZiPlayer](https://github.com/ZiProject/ZiPlayer)
|
|
520
|
+
- npm: [https://www.npmjs.com/package/ziplayer](https://www.npmjs.com/package/ziplayer)
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 📄 License
|
|
525
|
+
|
|
526
|
+
MIT License
|