ziplayer 0.2.7 → 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 -956
- package/README.md +277 -10
- package/dist/extensions/BaseExtension.d.ts +1 -0
- package/dist/extensions/BaseExtension.d.ts.map +1 -1
- package/dist/extensions/BaseExtension.js.map +1 -1
- package/dist/extensions/index.d.ts +38 -3
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +259 -41
- package/dist/extensions/index.js.map +1 -1
- package/dist/persistence/PersistenceManager.d.ts +95 -0
- package/dist/persistence/PersistenceManager.d.ts.map +1 -0
- package/dist/persistence/PersistenceManager.js +975 -0
- package/dist/persistence/PersistenceManager.js.map +1 -0
- package/dist/plugins/BasePlugin.js +1 -1
- package/dist/plugins/BasePlugin.js.map +1 -1
- package/dist/plugins/index.d.ts +73 -8
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +647 -116
- package/dist/plugins/index.js.map +1 -1
- package/dist/structures/FilterManager.js +3 -3
- package/dist/structures/FilterManager.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 +157 -14
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +1163 -188
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +106 -91
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +365 -124
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/Queue.d.ts +136 -31
- package/dist/structures/Queue.d.ts.map +1 -1
- package/dist/structures/Queue.js +265 -46
- package/dist/structures/Queue.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 +181 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/persistence.d.ts +77 -0
- package/dist/types/persistence.d.ts.map +1 -0
- package/dist/types/persistence.js +3 -0
- package/dist/types/persistence.js.map +1 -0
- package/package.json +3 -2
- package/src/extensions/BaseExtension.ts +1 -0
- package/src/extensions/index.ts +320 -37
- package/src/plugins/BasePlugin.ts +1 -1
- package/src/plugins/index.ts +801 -139
- package/src/structures/FilterManager.ts +3 -3
- package/src/structures/Player.ts +2797 -1693
- package/src/structures/PlayerManager.ts +438 -129
- package/src/structures/Queue.ts +300 -55
- 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 +187 -12
- package/src/types/plugin.ts +59 -59
- package/tsconfig.json +0 -1
package/AI-Guide.md
CHANGED
|
@@ -1,956 +1,624 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
###
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
player.
|
|
202
|
-
player.
|
|
203
|
-
player.
|
|
204
|
-
player.
|
|
205
|
-
player.
|
|
206
|
-
|
|
207
|
-
player.
|
|
208
|
-
player.
|
|
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
|
-
player.
|
|
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
|
-
player.
|
|
291
|
-
player.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
player.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
```typescript
|
|
482
|
-
import {
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
// Query
|
|
626
|
-
player.filter.hasFilter("nightcore"); // boolean
|
|
627
|
-
player.filter.getActiveFilters(); // AudioFilter[]
|
|
628
|
-
player.filter.getFilterString(); // raw FFmpeg string
|
|
629
|
-
player.filter.getAvailableFilters(); // all predefined
|
|
630
|
-
player.filter.getFiltersByCategory("eq");
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
---
|
|
634
|
-
|
|
635
|
-
## 10. Events Reference
|
|
636
|
-
|
|
637
|
-
### Manager events (recommended — all player events forwarded here)
|
|
638
|
-
|
|
639
|
-
```typescript
|
|
640
|
-
manager.on("trackStart", (player, track) => {});
|
|
641
|
-
manager.on("trackEnd", (player, track) => {});
|
|
642
|
-
manager.on("queueEnd", (player) => {});
|
|
643
|
-
manager.on("willPlay", (player, track, upcomingTracks) => {});
|
|
644
|
-
manager.on("queueAdd", (player, track) => {});
|
|
645
|
-
manager.on("queueAddList", (player, tracks) => {});
|
|
646
|
-
manager.on("queueRemove", (player, track, index) => {});
|
|
647
|
-
manager.on("playerPause", (player, track) => {});
|
|
648
|
-
manager.on("playerResume", (player, track) => {});
|
|
649
|
-
manager.on("playerStop", (player) => {});
|
|
650
|
-
manager.on("playerDestroy", (player) => {});
|
|
651
|
-
manager.on("playerError", (player, error, track?) => {});
|
|
652
|
-
manager.on("connectionError", (player, error) => {});
|
|
653
|
-
manager.on("volumeChange", (player, oldVolume, newVolume) => {});
|
|
654
|
-
manager.on("ttsStart", (player, { track }) => {});
|
|
655
|
-
manager.on("ttsEnd", (player) => {});
|
|
656
|
-
manager.on("voiceCreate", (player, evt) => {}); // voiceExt
|
|
657
|
-
manager.on("lyricsCreate", (player, track, result) => {}); // lyricsExt
|
|
658
|
-
manager.on("lyricsChange", (player, track, result) => {}); // lyricsExt
|
|
659
|
-
manager.on("debug", (message, ...args) => {});
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
### Direct player events
|
|
663
|
-
|
|
664
|
-
```typescript
|
|
665
|
-
player.on("trackStart", (track) => {});
|
|
666
|
-
player.on("trackEnd", (track) => {});
|
|
667
|
-
player.on("queueEnd", () => {});
|
|
668
|
-
player.on("willPlay", (track, upcomingTracks) => {});
|
|
669
|
-
player.on("playerError", (error, track?) => {});
|
|
670
|
-
player.on("ttsStart", ({ track }) => {});
|
|
671
|
-
player.on("ttsEnd", () => {});
|
|
672
|
-
player.on("debug", (message) => {});
|
|
673
|
-
// ... same names as manager, minus the leading `player` param
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
---
|
|
677
|
-
|
|
678
|
-
## 11. TypeScript Types
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
interface Track {
|
|
682
|
-
id: string;
|
|
683
|
-
title: string;
|
|
684
|
-
url: string;
|
|
685
|
-
duration: number; // milliseconds (some plugins use seconds — check source)
|
|
686
|
-
thumbnail?: string;
|
|
687
|
-
requestedBy: string;
|
|
688
|
-
source: string; // plugin name: "youtube" | "soundcloud" | "tts" | ...
|
|
689
|
-
metadata?: Record<string, any>;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
interface SearchResult {
|
|
693
|
-
tracks: Track[];
|
|
694
|
-
playlist?: { name: string; url: string; thumbnail?: string };
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
interface StreamInfo {
|
|
698
|
-
stream: Readable;
|
|
699
|
-
type: "webm/opus" | "ogg/opus" | "arbitrary";
|
|
700
|
-
metadata?: Record<string, any>;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
type LoopMode = "off" | "track" | "queue";
|
|
704
|
-
|
|
705
|
-
interface AudioFilter {
|
|
706
|
-
name: string;
|
|
707
|
-
ffmpegFilter: string;
|
|
708
|
-
description: string;
|
|
709
|
-
category?: string;
|
|
710
|
-
}
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
---
|
|
714
|
-
|
|
715
|
-
## 12. Common Patterns & Recipes
|
|
716
|
-
|
|
717
|
-
### Basic Discord bot setup (TypeScript)
|
|
718
|
-
|
|
719
|
-
```typescript
|
|
720
|
-
import { Client, GatewayIntentBits } from "discord.js";
|
|
721
|
-
import { PlayerManager } from "ziplayer";
|
|
722
|
-
import { YouTubePlugin, SoundCloudPlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
723
|
-
|
|
724
|
-
const client = new Client({
|
|
725
|
-
intents: [
|
|
726
|
-
GatewayIntentBits.Guilds,
|
|
727
|
-
GatewayIntentBits.GuildVoiceStates,
|
|
728
|
-
GatewayIntentBits.GuildMessages,
|
|
729
|
-
GatewayIntentBits.MessageContent,
|
|
730
|
-
],
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
const manager = new PlayerManager({
|
|
734
|
-
plugins: [new YouTubePlugin({}), new SoundCloudPlugin(), new SpotifyPlugin()],
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
manager.on("trackStart", (player, track) => {
|
|
738
|
-
(player.userdata?.channel as any)?.send(`▶ **${track.title}**`);
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
client.on("messageCreate", async (msg) => {
|
|
742
|
-
if (msg.author.bot || !msg.guildId) return;
|
|
743
|
-
if (!msg.content.startsWith("!play ")) return;
|
|
744
|
-
|
|
745
|
-
const query = msg.content.slice(6).trim();
|
|
746
|
-
const voiceChannel = (msg.member as any)?.voice?.channel;
|
|
747
|
-
if (!voiceChannel) return msg.reply("Join a voice channel first!");
|
|
748
|
-
|
|
749
|
-
const player = await manager.create(msg.guildId, {
|
|
750
|
-
leaveOnEnd: true,
|
|
751
|
-
leaveTimeout: 30_000,
|
|
752
|
-
userdata: { channel: msg.channel },
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
if (!player.connection) await player.connect(voiceChannel);
|
|
756
|
-
await player.play(query, msg.author.id);
|
|
757
|
-
msg.reply(`Queued: **${query}**`);
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
client.login(process.env.DISCORD_TOKEN);
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
### TTS with music interrupt
|
|
764
|
-
|
|
765
|
-
```typescript
|
|
766
|
-
const player = await manager.create(guildId, {
|
|
767
|
-
tts: { createPlayer: true, interrupt: true, volume: 100 },
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
// This pauses music, speaks, then auto-resumes:
|
|
771
|
-
await player.play("tts: Now playing your requested song!", userId);
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
### Voice-controlled bot
|
|
775
|
-
|
|
776
|
-
```typescript
|
|
777
|
-
manager.on("voiceCreate", (player, evt) => {
|
|
778
|
-
const text = evt.content.toLowerCase();
|
|
779
|
-
|
|
780
|
-
if (text.includes("skip")) player.skip();
|
|
781
|
-
else if (text.includes("pause")) player.pause();
|
|
782
|
-
else if (text.includes("resume")) player.resume();
|
|
783
|
-
else if (text.includes("stop")) player.stop();
|
|
784
|
-
else if (text.startsWith("play ")) {
|
|
785
|
-
player.play(text.slice(5), evt.userId);
|
|
786
|
-
}
|
|
787
|
-
});
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
### Autoplay (related tracks)
|
|
791
|
-
|
|
792
|
-
```typescript
|
|
793
|
-
player.queue.autoPlay(true);
|
|
794
|
-
// When queue empties, willNextTrack() is used (set by generateWillNext internally)
|
|
795
|
-
// ZiPlayer auto-fetches related tracks via pluginManager.getRelatedTracks()
|
|
796
|
-
```
|
|
797
|
-
|
|
798
|
-
### Loop patterns
|
|
799
|
-
|
|
800
|
-
```typescript
|
|
801
|
-
player.loop("track"); // repeat current song forever
|
|
802
|
-
player.loop("queue"); // loop entire playlist
|
|
803
|
-
player.loop("off"); // no loop (default)
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
### Progress bar in embeds
|
|
807
|
-
|
|
808
|
-
```typescript
|
|
809
|
-
manager.on("trackStart", (player, track) => {
|
|
810
|
-
const progress = player.getProgressBar({
|
|
811
|
-
size: 20,
|
|
812
|
-
barChar: "▬",
|
|
813
|
-
progressChar: "🔘",
|
|
814
|
-
});
|
|
815
|
-
// "0:00 | ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬🔘 | 3:32"
|
|
816
|
-
});
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
### Search without playing
|
|
820
|
-
|
|
821
|
-
```typescript
|
|
822
|
-
const result = await player.search("lofi hip hop", userId);
|
|
823
|
-
// result.tracks[0].title, .duration, .thumbnail, .url, etc.
|
|
824
|
-
|
|
825
|
-
// Or via manager (no player needed):
|
|
826
|
-
const result = await manager.search("lofi hip hop", userId);
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
### Custom extension for auto-announce
|
|
830
|
-
|
|
831
|
-
```typescript
|
|
832
|
-
class AutoAnnounceExt extends BaseExtension {
|
|
833
|
-
name = "autoAnnounce";
|
|
834
|
-
version = "1.0.0";
|
|
835
|
-
player: Player | null = null;
|
|
836
|
-
|
|
837
|
-
active(ctx: any): boolean {
|
|
838
|
-
if (!this.player) this.player = ctx.player;
|
|
839
|
-
const p = this.player!;
|
|
840
|
-
if ((p as any).__announced) return true;
|
|
841
|
-
(p as any).__announced = true;
|
|
842
|
-
|
|
843
|
-
p.on("trackStart", (track) => {
|
|
844
|
-
p.userdata?.channel?.send(`▶ Now playing: **${track.title}**`);
|
|
845
|
-
});
|
|
846
|
-
p.on("queueEnd", () => {
|
|
847
|
-
p.userdata?.channel?.send("Queue finished.");
|
|
848
|
-
});
|
|
849
|
-
return true;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
const manager = new PlayerManager({ extensions: [new AutoAnnounceExt()] });
|
|
854
|
-
const player = await manager.create(guildId, { extensions: ["autoAnnounce"] });
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
### Getting global manager from anywhere
|
|
858
|
-
|
|
859
|
-
```typescript
|
|
860
|
-
import { getManager, getPlayer } from "ziplayer";
|
|
861
|
-
|
|
862
|
-
const manager = getManager(); // PlayerManager | null
|
|
863
|
-
const player = getPlayer("guild-id"); // Player | null
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
---
|
|
867
|
-
|
|
868
|
-
## 13. Error Handling
|
|
869
|
-
|
|
870
|
-
### Recommended pattern
|
|
871
|
-
|
|
872
|
-
```typescript
|
|
873
|
-
try {
|
|
874
|
-
await player.connect(voiceChannel);
|
|
875
|
-
const success = await player.play(query, userId);
|
|
876
|
-
if (!success) channel.send("❌ Could not play that.");
|
|
877
|
-
} catch (err) {
|
|
878
|
-
channel.send(`❌ Error: ${(err as Error).message}`);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
manager.on("playerError", (player, error, track) => {
|
|
882
|
-
console.error(`[${player.guildId}] Error on "${track?.title}":`, error.message);
|
|
883
|
-
// ZiPlayer auto-skips to next track after playerError
|
|
884
|
-
});
|
|
885
|
-
|
|
886
|
-
manager.on("connectionError", (player, error) => {
|
|
887
|
-
console.error(`[${player.guildId}] Voice error:`, error.message);
|
|
888
|
-
player.destroy();
|
|
889
|
-
});
|
|
890
|
-
```
|
|
891
|
-
|
|
892
|
-
### Plugin timeout
|
|
893
|
-
|
|
894
|
-
```typescript
|
|
895
|
-
// Per-player timeout for plugin operations:
|
|
896
|
-
const player = await manager.create(guildId, {
|
|
897
|
-
extractorTimeout: 15_000, // 15 seconds (default: 50000)
|
|
898
|
-
});
|
|
899
|
-
```
|
|
900
|
-
|
|
901
|
-
---
|
|
902
|
-
|
|
903
|
-
## 14. Anti-Patterns to Avoid
|
|
904
|
-
|
|
905
|
-
| ❌ Wrong | ✅ Correct |
|
|
906
|
-
| ------------------------------------------------------ | ------------------------------------------ |
|
|
907
|
-
| Reusing `player` after `player.destroy()` | Call `manager.create()` again |
|
|
908
|
-
| Creating a new `PlayerManager` per command | One manager for the whole bot |
|
|
909
|
-
| Not awaiting `player.connect()` before `player.play()` | Always `await connect()` first |
|
|
910
|
-
| Ignoring `playerError` events | Always attach an error handler |
|
|
911
|
-
| Calling `player.play()` with empty string | Validate input before calling |
|
|
912
|
-
| Setting `leaveTimeout: 0` | Use `leaveOnEnd: false` instead |
|
|
913
|
-
| Using `player.queue.next()` directly | Use `player.skip()` to preserve events |
|
|
914
|
-
| Forgetting `disconnect()` on bot shutdown | Call `manager.destroy()` in SIGINT handler |
|
|
915
|
-
|
|
916
|
-
```typescript
|
|
917
|
-
// Clean shutdown
|
|
918
|
-
process.on("SIGINT", () => {
|
|
919
|
-
manager.destroy();
|
|
920
|
-
client.destroy();
|
|
921
|
-
process.exit(0);
|
|
922
|
-
});
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
---
|
|
926
|
-
|
|
927
|
-
## Quick Reference Card
|
|
928
|
-
|
|
929
|
-
```
|
|
930
|
-
CREATE manager.create(guildId, opts) → Player
|
|
931
|
-
CONNECT player.connect(voiceChannel)
|
|
932
|
-
PLAY player.play(query | Track | SearchResult | null, userId?)
|
|
933
|
-
PAUSE player.pause()
|
|
934
|
-
RESUME player.resume()
|
|
935
|
-
SKIP player.skip(index?)
|
|
936
|
-
PREVIOUS player.previous()
|
|
937
|
-
STOP player.stop() // also clears queue
|
|
938
|
-
SEEK player.seek(ms)
|
|
939
|
-
VOLUME player.setVolume(0–200)
|
|
940
|
-
LOOP player.loop("off"|"track"|"queue")
|
|
941
|
-
SHUFFLE player.shuffle()
|
|
942
|
-
AUTOPLAY player.autoPlay(bool)
|
|
943
|
-
DESTROY player.destroy()
|
|
944
|
-
|
|
945
|
-
FILTER player.filter.applyFilter("bassboost")
|
|
946
|
-
player.filter.removeFilter("nightcore")
|
|
947
|
-
player.filter.clearAll()
|
|
948
|
-
|
|
949
|
-
QUEUE player.queue.size / isEmpty / currentTrack / nextTrack
|
|
950
|
-
player.queue.add(track) / insert(track, 0) / remove(index)
|
|
951
|
-
player.queue.getTracks() / getTrack(index)
|
|
952
|
-
|
|
953
|
-
INFO player.currentTrack / previousTrack / upcomingTracks
|
|
954
|
-
player.getProgressBar() / getTime()
|
|
955
|
-
player.isPlaying / isPaused / volume
|
|
956
|
-
```
|
|
1
|
+
# 🤖 AI Guide for ZiPlayer
|
|
2
|
+
|
|
3
|
+
A comprehensive guide for AI assistants and developers working with ZiPlayer - a powerful Discord music player library.
|
|
4
|
+
|
|
5
|
+
## 📋 Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Project Overview](#project-overview)
|
|
8
|
+
2. [Architecture](#architecture)
|
|
9
|
+
3. [Core Concepts](#core-concepts)
|
|
10
|
+
4. [API Reference](#api-reference)
|
|
11
|
+
5. [Common Patterns](#common-patterns)
|
|
12
|
+
6. [Troubleshooting](#troubleshooting)
|
|
13
|
+
7. [Code Examples](#code-examples)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 🎯 Project Overview
|
|
18
|
+
|
|
19
|
+
**ZiPlayer** is an extensible Discord music engine built on `@discordjs/voice`.
|
|
20
|
+
|
|
21
|
+
### Key Features
|
|
22
|
+
|
|
23
|
+
- Plugin-driven architecture (YouTube, SoundCloud, Spotify, TTS)
|
|
24
|
+
- Extension system (voice commands, lyrics, Lavalink)
|
|
25
|
+
- Audio filters (bassboost, nightcore, etc.)
|
|
26
|
+
- Smart caching and fallback system
|
|
27
|
+
|
|
28
|
+
### Tech Stack
|
|
29
|
+
|
|
30
|
+
- TypeScript
|
|
31
|
+
- `@discordjs/voice` for audio
|
|
32
|
+
- FFmpeg for audio processing
|
|
33
|
+
- Node.js EventEmitter for events
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### Component Responsibilities
|
|
38
|
+
|
|
39
|
+
| Component | Responsibility |
|
|
40
|
+
| ------------------ | -------------------------------------------------- |
|
|
41
|
+
| `PlayerManager` | Creates/manages players, global event bus |
|
|
42
|
+
| `Player` | Per-guild audio playback, controls, event emission |
|
|
43
|
+
| `Queue` | Track management, loop modes, history, auto-play |
|
|
44
|
+
| `PluginManager` | Audio source resolution, streaming, fallback logic |
|
|
45
|
+
| `ExtensionManager` | Custom hooks (search, stream, before/after play) |
|
|
46
|
+
| `FilterManager` | FFmpeg audio effects |
|
|
47
|
+
| `StreamManager` | Centralized stream management/Auto cleanup |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🧠 Core Concepts
|
|
52
|
+
|
|
53
|
+
### 1. Player Lifecycle
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Create → Connect → Play → (Auto-save) → Destroy
|
|
57
|
+
const player = await manager.create(guildId, options);
|
|
58
|
+
await player.connect(voiceChannel);
|
|
59
|
+
await player.play(query, userId);
|
|
60
|
+
// ... auto-saves periodically
|
|
61
|
+
player.destroy();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Queue Loop Modes
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
player.loop("off"); // No loop (default)
|
|
68
|
+
player.loop("track"); // Repeat current track
|
|
69
|
+
player.loop("queue"); // Repeat entire queue
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3. Event Flow
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
trackStart → playing → trackEnd → playNext → (loop/autoplay)
|
|
76
|
+
↓
|
|
77
|
+
queueEnd → leave
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. Plugin Priority & Fallback
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Plugins are tried in priority order (higher = first)
|
|
84
|
+
// If primary fails, fallback plugins are attempted sequentially
|
|
85
|
+
// Failed plugins don't block the queue
|
|
86
|
+
|
|
87
|
+
plugin.priority = 10; // Higher priority
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 5. Caching Strategy
|
|
91
|
+
|
|
92
|
+
| Cache Type | TTL | Purpose |
|
|
93
|
+
| --------------- | ------- | --------------------------- |
|
|
94
|
+
| Search cache | 2 min | Avoid duplicate API calls |
|
|
95
|
+
| Stream cache | 5 min | Cache resolved streams |
|
|
96
|
+
| Extension cache | 1-5 min | Extension operation results |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 📚 API Reference
|
|
101
|
+
|
|
102
|
+
### PlayerManager
|
|
103
|
+
|
|
104
|
+
#### Constructor Options
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
interface PlayerManagerOptions {
|
|
108
|
+
plugins?: SourcePlugin[]; // Audio source plugins
|
|
109
|
+
extensions?: BaseExtension[]; // Custom extensions
|
|
110
|
+
extractorTimeout?: number; // Default: 10000ms
|
|
111
|
+
autoCleanup?: boolean; // Default: true
|
|
112
|
+
cleanupInterval?: number; // Default: 60000ms
|
|
113
|
+
enableSearchCache?: boolean; // Default: true
|
|
114
|
+
enableStatsCollection?: boolean; // Default: false
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Player Runtime Options (Performance Profile)
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
interface PlayerOptions {
|
|
122
|
+
lowPerformance?: boolean; // Default: false (or true when quality === "low")
|
|
123
|
+
preload?: {
|
|
124
|
+
enabled?: boolean; // Default: true
|
|
125
|
+
autoDisableInLowPerformance?: boolean; // Default: true
|
|
126
|
+
};
|
|
127
|
+
crossfade?: {
|
|
128
|
+
enabled?: boolean; // Explicit on/off
|
|
129
|
+
autoEnable?: boolean; // Default: true when enabled is undefined
|
|
130
|
+
autoDisableInLowPerformance?: boolean; // Default: true
|
|
131
|
+
durationMs?: number; // Default: 5000
|
|
132
|
+
};
|
|
133
|
+
smartTransition?: {
|
|
134
|
+
enabled?: boolean;
|
|
135
|
+
genreAware?: boolean;
|
|
136
|
+
beatAlign?: boolean;
|
|
137
|
+
baseDurationMs?: number;
|
|
138
|
+
minDurationMs?: number;
|
|
139
|
+
maxDurationMs?: number;
|
|
140
|
+
genreDurations?: Record<string, number>;
|
|
141
|
+
beatAlignMaxWaitMs?: number;
|
|
142
|
+
};
|
|
143
|
+
antiStuck?: {
|
|
144
|
+
enabled?: boolean;
|
|
145
|
+
maxRetries?: number;
|
|
146
|
+
retryDelayMs?: number;
|
|
147
|
+
reusePreloadFirst?: boolean;
|
|
148
|
+
reduceQualityOnRetry?: boolean;
|
|
149
|
+
controlledSkipThreshold?: number;
|
|
150
|
+
};
|
|
151
|
+
loudnessNormalization?: {
|
|
152
|
+
enabled?: boolean;
|
|
153
|
+
targetLUFS?: number;
|
|
154
|
+
maxBoostDb?: number;
|
|
155
|
+
maxCutDb?: number;
|
|
156
|
+
limiterCeiling?: number;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- If `lowPerformance=true`, preload and crossfade are auto-disabled by default.
|
|
162
|
+
- `crossfade.autoEnable=true` allows crossfade to be enabled automatically when `crossfade.enabled` is not explicitly set.
|
|
163
|
+
- You can still force behavior by setting `enabled` flags directly.
|
|
164
|
+
- Runtime behavior: crossfade is used for next-track transitions and `skip()`.
|
|
165
|
+
- Smart transition can tune fade by `metadata.genre` and beat-align by `metadata.bpm`.
|
|
166
|
+
- Loudness normalization uses `metadata.lufs` with limiter ceiling protection.
|
|
167
|
+
- Anti-stuck retries in-place before controlled skip to avoid skip storms.
|
|
168
|
+
|
|
169
|
+
#### Key Methods
|
|
170
|
+
|
|
171
|
+
| Method | Description |
|
|
172
|
+
| ---------------------------- | -------------------------- |
|
|
173
|
+
| `create(guildId, options)` | Create new player |
|
|
174
|
+
| `get(guildId)` | Get existing player |
|
|
175
|
+
| `delete(guildId)` | Destroy and remove player |
|
|
176
|
+
| `getAll()` | Get all players |
|
|
177
|
+
| `broadcast(action, ...args)` | Send action to all players |
|
|
178
|
+
|
|
179
|
+
### Player
|
|
180
|
+
|
|
181
|
+
#### Core Methods
|
|
182
|
+
|
|
183
|
+
| Method | Description | Returns |
|
|
184
|
+
| ------------------------------ | ----------------------- | ------------------- |
|
|
185
|
+
| `play(query, userId)` | Play track/search/queue | `Promise<boolean>` |
|
|
186
|
+
| `pause()` | Pause current | `boolean` |
|
|
187
|
+
| `resume()` | Resume playback | `boolean` |
|
|
188
|
+
| `skip(index?)` | Skip to next/index | `boolean` |
|
|
189
|
+
| `stop()` | Stop and clear queue | `boolean` |
|
|
190
|
+
| `seek(position)` | Seek to position (ms) | `Promise<boolean>` |
|
|
191
|
+
| `previous()` | Play previous track | `Promise<boolean>` |
|
|
192
|
+
| `setVolume(vol)` | Set volume (0-200) | `boolean` |
|
|
193
|
+
| `loop(mode)` | Set loop mode | `LoopMode` |
|
|
194
|
+
| `shuffle()` | Shuffle queue | `void` |
|
|
195
|
+
| `insert(query, index, userId)` | Insert at position | `Promise<boolean>` |
|
|
196
|
+
| `save(track, options)` | Save track to stream | `Promise<Readable>` |
|
|
197
|
+
|
|
198
|
+
#### Getters
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
player.currentTrack; // Track | null
|
|
202
|
+
player.queueSize; // number
|
|
203
|
+
player.isPlaying; // boolean
|
|
204
|
+
player.isPaused; // boolean
|
|
205
|
+
player.volume; // number
|
|
206
|
+
player.upcomingTracks; // Track[]
|
|
207
|
+
player.previousTracks; // Track[]
|
|
208
|
+
player.relatedTracks; // Track[] | null
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Queue
|
|
212
|
+
|
|
213
|
+
#### Methods
|
|
214
|
+
|
|
215
|
+
| Method | Description |
|
|
216
|
+
| ------------------------- | -------------------- |
|
|
217
|
+
| `add(track)` | Add single track |
|
|
218
|
+
| `addMultiple(tracks)` | Add multiple tracks |
|
|
219
|
+
| `insert(track, index)` | Insert at position |
|
|
220
|
+
| `remove(index)` | Remove at index |
|
|
221
|
+
| `removeMultiple(indices)` | Remove multiple |
|
|
222
|
+
| `removeWhere(predicate)` | Remove by condition |
|
|
223
|
+
| `move(from, to)` | Move track |
|
|
224
|
+
| `swap(a, b)` | Swap tracks |
|
|
225
|
+
| `shuffle()` | Randomize order |
|
|
226
|
+
| `clear()` | Clear all tracks |
|
|
227
|
+
| `loop(mode)` | Set loop mode |
|
|
228
|
+
| `autoPlay(enabled)` | Enable/disable |
|
|
229
|
+
| `previous()` | Get previous track |
|
|
230
|
+
| `jumpToHistory(steps)` | Jump back in history |
|
|
231
|
+
|
|
232
|
+
#### Properties
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
queue.size; // number
|
|
236
|
+
queue.isEmpty; // boolean
|
|
237
|
+
queue.currentTrack; // Track | null
|
|
238
|
+
queue.nextTrack; // Track | null
|
|
239
|
+
queue.lastTrack; // Track | null
|
|
240
|
+
queue.previousTracks; // Track[]
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### FilterManager
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// Apply filters
|
|
247
|
+
await player.filter.applyFilter("bassboost");
|
|
248
|
+
await player.filter.applyFilters(["bassboost", "nightcore"]);
|
|
249
|
+
|
|
250
|
+
// Available filters
|
|
251
|
+
// bassboost, trebleboost, nightcore, lofi, vaporwave,
|
|
252
|
+
// echo, reverb, chorus, karaoke, normalize, compressor, limiter
|
|
253
|
+
|
|
254
|
+
// Clear filters
|
|
255
|
+
await player.filter.clearAll();
|
|
256
|
+
await player.filter.clear("bassboost");
|
|
257
|
+
|
|
258
|
+
// Get current filters
|
|
259
|
+
const filterString = player.filter.getFilterString(); // "bassboost,nightcore"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## 🔧 Common Patterns
|
|
263
|
+
|
|
264
|
+
### 1. Basic Music Bot Setup
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import { Client, GatewayIntentBits } from "discord.js";
|
|
268
|
+
import { PlayerManager } from "ziplayer";
|
|
269
|
+
import { YouTubePlugin, SpotifyPlugin } from "@ziplayer/plugin";
|
|
270
|
+
|
|
271
|
+
const client = new Client({
|
|
272
|
+
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates],
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const manager = new PlayerManager({
|
|
276
|
+
plugins: [new YouTubePlugin(), new SpotifyPlugin()],
|
|
277
|
+
autoCleanup: true,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
client.on("ready", async () => {
|
|
281
|
+
// Auto-load saved players
|
|
282
|
+
await manager.loadAllPlayers();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
client.on("messageCreate", async (msg) => {
|
|
286
|
+
if (msg.content.startsWith("!play")) {
|
|
287
|
+
const player = await manager.create(msg.guildId);
|
|
288
|
+
const voiceChannel = msg.member?.voice.channel;
|
|
289
|
+
|
|
290
|
+
if (!player.connection) {
|
|
291
|
+
await player.connect(voiceChannel);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const query = msg.content.slice(6);
|
|
295
|
+
await player.play(query, msg.author.id);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const player = await manager.create(guildId, {
|
|
300
|
+
lowPerformance: false,
|
|
301
|
+
preload: { enabled: true, autoDisableInLowPerformance: true },
|
|
302
|
+
crossfade: { autoEnable: true, autoDisableInLowPerformance: true, durationMs: 5000 },
|
|
303
|
+
smartTransition: {
|
|
304
|
+
enabled: true,
|
|
305
|
+
genreAware: true,
|
|
306
|
+
beatAlign: true,
|
|
307
|
+
baseDurationMs: 5000,
|
|
308
|
+
genreDurations: { chill: 7000, edm: 2200 },
|
|
309
|
+
},
|
|
310
|
+
antiStuck: {
|
|
311
|
+
enabled: true,
|
|
312
|
+
maxRetries: 2,
|
|
313
|
+
retryDelayMs: 900,
|
|
314
|
+
reusePreloadFirst: true,
|
|
315
|
+
reduceQualityOnRetry: true,
|
|
316
|
+
controlledSkipThreshold: 3,
|
|
317
|
+
},
|
|
318
|
+
loudnessNormalization: {
|
|
319
|
+
enabled: true,
|
|
320
|
+
targetLUFS: -14,
|
|
321
|
+
limiterCeiling: 0.95,
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 2. Progress Bar with Time Format
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// Get formatted time
|
|
330
|
+
const time = player.getTime();
|
|
331
|
+
console.log(`Current: ${time.formatted.current}`); // "1:22:12"
|
|
332
|
+
console.log(`Total: ${time.formatted.total}`); // "3:45:30"
|
|
333
|
+
|
|
334
|
+
// Progress bar
|
|
335
|
+
const progressBar = player.getProgressBar({
|
|
336
|
+
size: 20,
|
|
337
|
+
barChar: "▬",
|
|
338
|
+
progressChar: "🔘",
|
|
339
|
+
timeFormat: "compact",
|
|
340
|
+
showPercentage: true,
|
|
341
|
+
});
|
|
342
|
+
// Output: "1:22:12 ▬▬▬▬▬▬▬▬▬🔘▬▬▬▬▬▬▬▬ 3:45:30 (36%)"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### 3. Queue Management Commands
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// Skip to specific track
|
|
349
|
+
await player.skip(3); // Skip to index 3
|
|
350
|
+
|
|
351
|
+
// Move track to front
|
|
352
|
+
player.queue.move(5, 0);
|
|
353
|
+
|
|
354
|
+
// Remove all tracks from specific source
|
|
355
|
+
player.queue.removeWhere((t) => t.source === "soundcloud");
|
|
356
|
+
|
|
357
|
+
// Jump back 2 tracks
|
|
358
|
+
await player.queue.jumpToHistory(2);
|
|
359
|
+
|
|
360
|
+
// Insert as next track
|
|
361
|
+
await player.insert("song name", 0, userId);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 4. Custom Plugin Implementation
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import { BasePlugin, Track, StreamInfo } from "ziplayer";
|
|
368
|
+
|
|
369
|
+
class CustomPlugin extends BasePlugin {
|
|
370
|
+
name = "CustomPlugin";
|
|
371
|
+
priority = 5;
|
|
372
|
+
|
|
373
|
+
canHandle(query: string): boolean {
|
|
374
|
+
return query.startsWith("custom:");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async search(query: string, requestedBy: string): Promise<SearchResult> {
|
|
378
|
+
// Implementation
|
|
379
|
+
return { tracks: [] };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async getStream(track: Track, signal: AbortSignal): Promise<StreamInfo> {
|
|
383
|
+
// Return audio stream
|
|
384
|
+
return { stream: readableStream, type: "arbitrary" };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async getRelatedTracks(track: Track): Promise<Track[]> {
|
|
388
|
+
// Return recommendations
|
|
389
|
+
return [];
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### 5. Custom Extension Implementation
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
import { BaseExtension, ExtensionContext } from "ziplayer";
|
|
398
|
+
|
|
399
|
+
class LoggerExtension extends BaseExtension {
|
|
400
|
+
name = "Logger";
|
|
401
|
+
priority = 10;
|
|
402
|
+
|
|
403
|
+
async beforePlay(context: ExtensionContext, request: any) {
|
|
404
|
+
console.log(`Playing: ${request.query}`);
|
|
405
|
+
return { handled: false };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async afterPlay(context: ExtensionContext, payload: any) {
|
|
409
|
+
if (payload.success) {
|
|
410
|
+
console.log(`Successfully played ${payload.tracks?.length} tracks`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### 6. Event Handling
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
manager.on("trackStart", (player, track) => {
|
|
420
|
+
console.log(`Now playing: ${track.title}`);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
manager.on("queueEnd", (player) => {
|
|
424
|
+
console.log("Queue finished!");
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
manager.on("playerError", (player, error, track) => {
|
|
428
|
+
console.error(`Error on ${track?.title}:`, error.message);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
manager.on("playerSaved", (guildId) => {
|
|
432
|
+
console.log(`Saved state for guild ${guildId}`);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
manager.on("stats", (stats) => {
|
|
436
|
+
console.log(`Active players: ${stats.activePlayers}`);
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## 🐛 Troubleshooting
|
|
443
|
+
|
|
444
|
+
### Common Issues
|
|
445
|
+
|
|
446
|
+
| Issue | Solution |
|
|
447
|
+
| ------------------------ | ----------------------------------------------------------- |
|
|
448
|
+
| **No audio** | Check `player.connection` exists, voice channel permissions |
|
|
449
|
+
| **Plugin not working** | Verify `canHandle()` returns true, check priority |
|
|
450
|
+
| **Filters not applying** | Call `refreshPlayerResource(true)` after applying |
|
|
451
|
+
| **Memory leak** | Enable `autoCleanup`, call `player.destroy()` when done |
|
|
452
|
+
| **Rate limiting** | Use search cache, increase `extractorTimeout` |
|
|
453
|
+
|
|
454
|
+
### Debug Mode
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
// Enable debug logging
|
|
458
|
+
manager.on("debug", (message) => {
|
|
459
|
+
console.log("[DEBUG]", message);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Or check debug flag
|
|
463
|
+
if (manager.debugEnabled) {
|
|
464
|
+
// Debug-specific logic
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Performance Tips
|
|
469
|
+
|
|
470
|
+
1. **Enable caching** for search and stream results
|
|
471
|
+
2. **Set appropriate timeouts** based on network conditions
|
|
472
|
+
3. **Batch operations** when modifying queue
|
|
473
|
+
4. **Destroy players** when no longer needed
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## 📝 Code Examples
|
|
478
|
+
|
|
479
|
+
### Full Bot Example
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
import { Client, GatewayIntentBits, EmbedBuilder } from "discord.js";
|
|
483
|
+
import { PlayerManager } from "ziplayer";
|
|
484
|
+
import { YouTubePlugin, SpotifyPlugin, TTSPlugin } from "@ziplayer/plugin";
|
|
485
|
+
|
|
486
|
+
const client = new Client({
|
|
487
|
+
intents: [
|
|
488
|
+
GatewayIntentBits.Guilds,
|
|
489
|
+
GatewayIntentBits.GuildVoiceStates,
|
|
490
|
+
GatewayIntentBits.GuildMessages,
|
|
491
|
+
GatewayIntentBits.MessageContent,
|
|
492
|
+
],
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
const manager = new PlayerManager({
|
|
496
|
+
plugins: [new YouTubePlugin(), new SpotifyPlugin(), new TTSPlugin()],
|
|
497
|
+
autoCleanup: true,
|
|
498
|
+
extractorTimeout: 30000,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
client.on("messageCreate", async (msg) => {
|
|
502
|
+
if (!msg.guildId || msg.author.bot) return;
|
|
503
|
+
|
|
504
|
+
const args = msg.content.slice(1).split(" ");
|
|
505
|
+
const command = args[0].toLowerCase();
|
|
506
|
+
const query = args.slice(1).join(" ");
|
|
507
|
+
|
|
508
|
+
const player = await manager.create(msg.guildId);
|
|
509
|
+
const voiceChannel = msg.member?.voice.channel;
|
|
510
|
+
|
|
511
|
+
switch (command) {
|
|
512
|
+
case "play":
|
|
513
|
+
if (!voiceChannel) return msg.reply("Join a voice channel!");
|
|
514
|
+
if (!player.connection) await player.connect(voiceChannel);
|
|
515
|
+
await player.play(query, msg.author.id);
|
|
516
|
+
break;
|
|
517
|
+
|
|
518
|
+
case "pause":
|
|
519
|
+
player.pause();
|
|
520
|
+
break;
|
|
521
|
+
|
|
522
|
+
case "resume":
|
|
523
|
+
player.resume();
|
|
524
|
+
break;
|
|
525
|
+
|
|
526
|
+
case "skip":
|
|
527
|
+
player.skip();
|
|
528
|
+
break;
|
|
529
|
+
|
|
530
|
+
case "stop":
|
|
531
|
+
player.stop();
|
|
532
|
+
break;
|
|
533
|
+
|
|
534
|
+
case "volume":
|
|
535
|
+
const vol = parseInt(query);
|
|
536
|
+
if (isNaN(vol)) return msg.reply("Volume must be a number!");
|
|
537
|
+
player.setVolume(vol);
|
|
538
|
+
break;
|
|
539
|
+
|
|
540
|
+
case "queue":
|
|
541
|
+
const tracks = player.upcomingTracks.slice(0, 10);
|
|
542
|
+
const embed = new EmbedBuilder()
|
|
543
|
+
.setTitle("Queue")
|
|
544
|
+
.setDescription(tracks.map((t, i) => `${i + 1}. ${t.title}`).join("\n") || "Empty");
|
|
545
|
+
msg.reply({ embeds: [embed] });
|
|
546
|
+
break;
|
|
547
|
+
|
|
548
|
+
case "nowplaying":
|
|
549
|
+
const track = player.currentTrack;
|
|
550
|
+
if (!track) return msg.reply("Nothing playing!");
|
|
551
|
+
|
|
552
|
+
const progress = player.getProgressBar({ size: 15 });
|
|
553
|
+
const time = player.getTime();
|
|
554
|
+
|
|
555
|
+
const embed = new EmbedBuilder()
|
|
556
|
+
.setTitle(track.title)
|
|
557
|
+
.setURL(track.url)
|
|
558
|
+
.setThumbnail(track.thumbnail)
|
|
559
|
+
.setDescription(`\`${progress}\`\n${time.formatted.current} / ${time.formatted.total}`);
|
|
560
|
+
msg.reply({ embeds: [embed] });
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
client.login(process.env.DISCORD_TOKEN);
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## 🔗 Quick Reference
|
|
571
|
+
|
|
572
|
+
### Import Paths
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
// Core
|
|
576
|
+
import { PlayerManager, Player, Queue } from "ziplayer";
|
|
577
|
+
|
|
578
|
+
// Types
|
|
579
|
+
import type { Track, SearchResult, LoopMode, StreamInfo } from "ziplayer";
|
|
580
|
+
|
|
581
|
+
// Plugins (external package)
|
|
582
|
+
import { YouTubePlugin, SpotifyPlugin, TTSPlugin } from "@ziplayer/plugin";
|
|
583
|
+
|
|
584
|
+
// infinity plugin support stream audio from YouTube, TikTok, Instagram, Twitter/X, SoundCloud, Reddit, Twitch, Bilibili, and 1000+ other sites
|
|
585
|
+
|
|
586
|
+
import { InfinityPlugin } from "@ziplayer/infinity";
|
|
587
|
+
|
|
588
|
+
// Extensions (external package)
|
|
589
|
+
import { voiceExt, lyricsExt, lavalinkExt } from "@ziplayer/extension";
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Type Definitions
|
|
593
|
+
|
|
594
|
+
```typescript
|
|
595
|
+
interface Track {
|
|
596
|
+
id: string;
|
|
597
|
+
title: string;
|
|
598
|
+
url: string;
|
|
599
|
+
source: string;
|
|
600
|
+
duration: number;
|
|
601
|
+
thumbnail?: string;
|
|
602
|
+
requestedBy?: string;
|
|
603
|
+
isLive?: boolean;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
type LoopMode = "off" | "track" | "queue";
|
|
607
|
+
|
|
608
|
+
interface SearchResult {
|
|
609
|
+
tracks: Track[];
|
|
610
|
+
playlist?: { name: string; url?: string };
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## 📖 Additional Resources
|
|
617
|
+
|
|
618
|
+
- [GitHub Repository](https://github.com/ZiProject/ZiPlayer)
|
|
619
|
+
- [npm Package](https://www.npmjs.com/package/ziplayer)
|
|
620
|
+
- [Examples Folder](https://github.com/ZiProject/ZiPlayer/tree/main/examples)
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
_This guide is maintained for AI assistants and developers. For questions or contributions, please open an issue on GitHub._
|