ziplayer 0.2.6 → 0.2.7-dev.1

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 (57) hide show
  1. package/AI-Guide.md +607 -0
  2. package/README.md +513 -196
  3. package/dist/extensions/BaseExtension.d.ts +1 -0
  4. package/dist/extensions/BaseExtension.d.ts.map +1 -1
  5. package/dist/extensions/BaseExtension.js.map +1 -1
  6. package/dist/extensions/index.d.ts +38 -3
  7. package/dist/extensions/index.d.ts.map +1 -1
  8. package/dist/extensions/index.js +259 -41
  9. package/dist/extensions/index.js.map +1 -1
  10. package/dist/persistence/PersistenceManager.d.ts +61 -0
  11. package/dist/persistence/PersistenceManager.d.ts.map +1 -0
  12. package/dist/persistence/PersistenceManager.js +551 -0
  13. package/dist/persistence/PersistenceManager.js.map +1 -0
  14. package/dist/plugins/BasePlugin.js +1 -1
  15. package/dist/plugins/BasePlugin.js.map +1 -1
  16. package/dist/plugins/index.d.ts +19 -4
  17. package/dist/plugins/index.d.ts.map +1 -1
  18. package/dist/plugins/index.js +273 -146
  19. package/dist/plugins/index.js.map +1 -1
  20. package/dist/structures/FilterManager.js +3 -3
  21. package/dist/structures/FilterManager.js.map +1 -1
  22. package/dist/structures/Player.d.ts +64 -14
  23. package/dist/structures/Player.d.ts.map +1 -1
  24. package/dist/structures/Player.js +344 -91
  25. package/dist/structures/Player.js.map +1 -1
  26. package/dist/structures/PlayerManager.d.ts +125 -91
  27. package/dist/structures/PlayerManager.d.ts.map +1 -1
  28. package/dist/structures/PlayerManager.js +406 -111
  29. package/dist/structures/PlayerManager.js.map +1 -1
  30. package/dist/structures/Queue.d.ts +136 -31
  31. package/dist/structures/Queue.d.ts.map +1 -1
  32. package/dist/structures/Queue.js +265 -46
  33. package/dist/structures/Queue.js.map +1 -1
  34. package/dist/types/index.d.ts +39 -6
  35. package/dist/types/index.d.ts.map +1 -1
  36. package/dist/types/index.js +1 -0
  37. package/dist/types/index.js.map +1 -1
  38. package/dist/types/persistence.d.ts +55 -0
  39. package/dist/types/persistence.d.ts.map +1 -0
  40. package/dist/types/persistence.js +3 -0
  41. package/dist/types/persistence.js.map +1 -0
  42. package/package.json +47 -46
  43. package/src/extensions/BaseExtension.ts +36 -35
  44. package/src/extensions/index.ts +473 -190
  45. package/src/index.ts +16 -16
  46. package/src/persistence/PersistenceManager.ts +572 -0
  47. package/src/plugins/BasePlugin.ts +27 -27
  48. package/src/plugins/index.ts +403 -236
  49. package/src/structures/FilterManager.ts +303 -303
  50. package/src/structures/Player.ts +1962 -1689
  51. package/src/structures/PlayerManager.ts +788 -416
  52. package/src/structures/Queue.ts +599 -354
  53. package/src/types/index.ts +406 -373
  54. package/src/types/persistence.ts +65 -0
  55. package/src/types/plugin.ts +1 -1
  56. package/src/utils/timeout.ts +10 -10
  57. package/tsconfig.json +22 -23
package/README.md CHANGED
@@ -1,196 +1,513 @@
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 modular Discord voice player with plugin system for @discordjs/voice.
6
-
7
- ## Features
8
-
9
- - 🎵 **Plugin-based architecture** - Easy to extend with new sources
10
- - 🎶 **Multiple source support** - YouTube, SoundCloud, Spotify (with fallback)
11
- - 🔊 **Queue management** - Add, remove, shuffle, clear
12
- - 🎚️ **Volume control** - 0-200% volume range
13
- - ⏯️ **Playback control** - Play, pause, resume, stop, skip
14
- - 🔁 **Auto play** - Automatically replay the queue when it ends
15
- - 🔂 **Loop control** - Repeat a single track or the entire queue
16
- - 📊 **Progress bar** - Display playback progress with customizable icons
17
- - 🔔 **Event-driven** - Rich event system for all player actions
18
- - 🎭 **Multi-guild support** - Manage players across multiple Discord servers
19
- - 🗃️ **User data** - Attach custom data to each player for later use
20
- - 🔌 **Lavalink** - Support manage an external Lavalink JVM node
21
- - 🎛️ **Audio Filters** - Apply real-time audio effects using FFmpeg (bassboost, nightcore, etc.)
22
-
23
- ## Installation
24
-
25
- ```bash
26
- npm install ziplayer @ziplayer/plugin @ziplayer/extension @discordjs/voice discord.js
27
- ```
28
-
29
- ## Quick Start
30
-
31
- ```typescript
32
- import { PlayerManager } from "ziplayer";
33
- import { SoundCloudPlugin, YouTubePlugin, SpotifyPlugin } from "@ziplayer/plugin";
34
- import { voiceExt } from "@ziplayer/extension";
35
-
36
- const manager = new PlayerManager({
37
- plugins: [new SoundCloudPlugin(), new YouTubePlugin(), new SpotifyPlugin()],
38
- extensions: [new voiceExt()],
39
- });
40
-
41
- // Create player
42
- const player = await manager.create(guildId, {
43
- leaveOnEnd: true,
44
- leaveTimeout: 30000,
45
- userdata: { channel: textChannel }, // store channel for events
46
- // Choose extensions for this player (by name or instances)
47
- // extensions: ["voiceExt"],
48
- // Apply audio filters
49
- // filters: ["bassboost", "normalize"],
50
- });
51
-
52
- // Connect and play
53
- await player.connect(voiceChannel);
54
- await player.play("Never Gonna Give You Up", userId);
55
-
56
- // Play a full YouTube playlist
57
- await player.play("https://www.youtube.com/playlist?list=PL123", userId);
58
-
59
- // Enable autoplay
60
- player.queue.autoPlay(true);
61
-
62
- // Play a full SoundCloud playlist
63
- await player.play("https://soundcloud.com/artist/sets/playlist", userId);
64
-
65
- // Events
66
- player.on("willPlay", (player, track) => {
67
- console.log(`Up next: ${track.title}`);
68
- });
69
- player.on("trackStart", (player, track) => {
70
- console.log(`Now playing: ${track.title}`);
71
- player.userdata?.channel?.send(`Now playing: ${track.title}`);
72
- });
73
-
74
- // Audio Filters
75
- player.filter.applyFilter("bassboost"); // Apply bass boost
76
- player.filter.applyFilter("nightcore"); // Apply nightcore effect
77
- player.filter.removeFilter("bassboost"); // Remove specific filter
78
- player.filter.clearFilters(); // Clear all filters
79
-
80
- // Apply custom filter
81
- player.filter.applyFilter({
82
- name: "custom",
83
- ffmpegFilter: "volume=1.5,treble=g=5",
84
- description: "Volume boost + treble boost",
85
- });
86
-
87
- // Receive transcripts
88
- manager.on("voiceCreate", (player, evt) => {
89
- console.log(`User ${evt.userId} said: ${evt.content}`);
90
- });
91
- ```
92
-
93
- ### TTS (Interrupt Mode)
94
-
95
- Play short text-to-speech messages without losing music progress. The player pauses music, plays TTS on a dedicated AudioPlayer,
96
- then resumes.
97
-
98
- - Requirements: `@ziplayer/plugin` with `TTSPlugin` installed and registered in `PlayerManager`.
99
-
100
- ```ts
101
- import { PlayerManager } from "ziplayer";
102
- import { TTSPlugin, YouTubePlugin, SoundCloudPlugin, SpotifyPlugin } from "@ziplayer/plugin";
103
-
104
- const manager = new PlayerManager({
105
- plugins: [new TTSPlugin({ defaultLang: "vi" }), new YouTubePlugin(), new SoundCloudPlugin(), new SpotifyPlugin()],
106
- });
107
-
108
- // Create a player with TTS interrupt enabled
109
- const player = await manager.create(guildId, {
110
- tts: {
111
- createPlayer: true, // pre-create the internal TTS AudioPlayer
112
- interrupt: true, // pause music, swap to TTS, then resume
113
- volume: 1, // 1 => 100%
114
- },
115
- });
116
-
117
- await player.connect(voiceChannel);
118
-
119
- // Trigger TTS by playing a TTS query (depends on your TTS plugin)
120
- await player.play("tts: xin chào mọi người", userId);
121
-
122
- // Listen to TTS lifecycle events
123
- manager.on("ttsStart", (plr, { track }) => console.log("TTS start", track?.title));
124
- manager.on("ttsEnd", (plr) => console.log("TTS end"));
125
- ```
126
-
127
- Notes
128
-
129
- - The detection uses track.source that includes "tts" or query starting with `tts:`.
130
- - If you need more control, call `player.interruptWithTTSTrack(track)` after building a TTS track via your plugin.
131
-
132
- ### extensions and Lavalink Process
133
-
134
- Use `lavalinkExt` when you need ZiPlayer to manage an external Lavalink JVM node. The extension starts, stops, and optionally
135
- restarts the Lavalink jar and forwards lifecycle events through the manager/player.
136
-
137
- ```ts
138
- import { PlayerManager } from "ziplayer";
139
- import { lavalinkExt, lyricsExt, voiceExt } from "@ziplayer/extension";
140
-
141
- const manager = new PlayerManager({
142
- extensions: [
143
- new lavalinkExt(null, {
144
- nodes: [
145
- {
146
- identifier: "locallavalink",
147
- password: "youshallnotpass",
148
- host: "localhost",
149
- port: 2333,
150
- secure: false,
151
- },
152
- ],
153
- client: client,
154
- searchPrefix: "scsearch",
155
- }),
156
- new voiceExt(null, { lang: "en-US" }),
157
- new lyricsExt(null, { provider: "lrclib" }),
158
- ],
159
- //etc...
160
- });
161
-
162
- //crete player:
163
- const player = await manager.create("id-player", {
164
- extensions: ["lavalinkExt", "voiceExt", "lyricsExt"],
165
- //etc... userdata,
166
- });
167
-
168
- //connec voice
169
- if (!player.connection) await player.connect(interaction?.member?.voice?.channel);
170
-
171
- //play music
172
- await player.play(query, interaction?.user);
173
- ```
174
-
175
- ## Events
176
-
177
- All player events are forwarded through the PlayerManager:
178
-
179
- - `trackStart` - When a track starts playing
180
- - `willPlay` - Before a track begins playing
181
- - `trackEnd` - When a track finishes
182
- - `queueEnd` - When the queue is empty
183
- - `playerError` - When an error occurs
184
- - `queueAdd` - When a track is added
185
- - `volumeChange` - When volume changes
186
- - And more...
187
-
188
- ## Useful Links
189
-
190
- [Example](https://github.com/ZiProject/ZiPlayer/tree/main/examples) | [Repo](https://github.com/ZiProject/ZiPlayer) |
191
- [Package](https://www.npmjs.com/package/ziplayer) | [Plugin](https://www.npmjs.com/package/@ziplayer/plugin) |
192
- [Extension](https://www.npmjs.com/package/@ziplayer/extension)
193
-
194
- ## License
195
-
196
- MIT License
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
+ - 💿 **Persistence** — Auto-save and restore player state across restarts
25
+ - ⚡ **Smart caching** — Search and stream caching for better performance
26
+ - 🎯 **Queue management** Advanced queue operations (move, swap, batch remove)
27
+
28
+ ---
29
+
30
+ ## 📦 Installation
31
+
32
+ ```bash
33
+ npm install ziplayer @ziplayer/plugin @ziplayer/extension @ziplayer/infinity @discordjs/voice discord.js opusscript
34
+ ```
35
+ ---
36
+
37
+ ## 🚀 Quick Start
38
+
39
+ ```ts
40
+ import { Client, GatewayIntentBits } from "discord.js";
41
+ import { PlayerManager } from "ziplayer";
42
+ import { YouTubePlugin, SoundCloudPlugin, SpotifyPlugin } from "@ziplayer/plugin";
43
+ import { InfinityPlugin } from "@ziplayer/infinity";
44
+
45
+ const client = new Client({
46
+ intents: [
47
+ GatewayIntentBits.Guilds,
48
+ GatewayIntentBits.GuildVoiceStates,
49
+ GatewayIntentBits.GuildMessages,
50
+ GatewayIntentBits.MessageContent,
51
+ ],
52
+ });
53
+
54
+ const manager = new PlayerManager({
55
+ plugins: [new YouTubePlugin(), new SoundCloudPlugin(), new SpotifyPlugin(), new InfinityPlugin()],
56
+ });
57
+
58
+ client.on("messageCreate", async (msg) => {
59
+ if (!msg.content.startsWith("!play ") || !msg.guildId) return;
60
+
61
+ const voiceChannel = msg.member?.voice?.channel;
62
+ if (!voiceChannel) return msg.reply("Join a voice channel first!");
63
+
64
+ const player = await manager.create(msg.guildId, {
65
+ leaveOnEnd: true,
66
+ userdata: { channel: msg.channel },
67
+ });
68
+
69
+ if (!player.connection) await player.connect(voiceChannel);
70
+ await player.play(msg.content.slice(6), msg.author.id);
71
+ });
72
+
73
+ client.login(process.env.DISCORD_TOKEN);
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 🧱 Architecture Overview
79
+
80
+ ```
81
+ PlayerManager (global)
82
+ ├── PersistenceManager (auto-save/load)
83
+ └── Player (per guild)
84
+ ├── Queue (advanced operations)
85
+ ├── PluginManager (with caching & fallback)
86
+ ├── ExtensionManager (with priority & caching)
87
+ └── FilterManager (FFmpeg filters)
88
+ ```
89
+
90
+ ### Flow
91
+
92
+ ```
93
+ create connect → play → stream → events → destroy
94
+
95
+ auto-save (periodic)
96
+
97
+ restore on restart
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 🎵 Core Usage
103
+
104
+ ### Play music
105
+
106
+ ```ts
107
+ await player.play("Never Gonna Give You Up", userId);
108
+ await player.play("https://youtube.com/watch?v=...", userId);
109
+ await player.play("tts: Hello world", userId);
110
+ await player.play(searchResult, userId); // Play from SearchResult
111
+ await player.play(null); // Resume from queue
112
+ ```
113
+
114
+ ### Controls
115
+
116
+ ```ts
117
+ player.pause();
118
+ player.resume();
119
+ player.skip();
120
+ player.skip(2); // Skip to track at index 2
121
+ player.stop();
122
+ player.setVolume(100);
123
+ player.loop("track"); // Loop current track
124
+ player.loop("queue"); // Loop entire queue
125
+ player.loop(1); // Number mode: 0=off, 1=track, 2=queue
126
+ player.shuffle();
127
+ player.seek(30000); // Seek to 30 seconds
128
+ player.previous(); // Go back to previous track
129
+ ```
130
+
131
+ ### Queue Management
132
+
133
+ ```ts
134
+ // Basic operations
135
+ player.queue.add(track);
136
+ player.queue.addMultiple([track1, track2]);
137
+ player.queue.remove(0);
138
+ player.queue.removeMultiple([0, 2, 5]); // Remove multiple indices
139
+ player.queue.removeWhere((t) => t.source === "youtube"); // Remove by condition
140
+ player.queue.clear();
141
+
142
+ // Queue manipulation
143
+ player.queue.move(3, 0); // Move track at index 3 to front
144
+ player.queue.swap(1, 3); // Swap positions 1 and 3
145
+ player.queue.shuffle();
146
+
147
+ // Queue inspection
148
+ player.queue.size;
149
+ player.queue.isEmpty;
150
+ player.queue.currentTrack;
151
+ player.queue.nextTrack;
152
+ player.queue.lastTrack;
153
+ player.queue.previousTracks;
154
+ player.queue.getTrack(5);
155
+ player.queue.findTracks((t) => t.duration > 300000);
156
+ player.queue.indexOf(track);
157
+ player.queue.has(track);
158
+
159
+ // History navigation
160
+ player.queue.jumpToHistory(2); // Go back 2 tracks
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 💾 Persistence (Auto-save & Restore)
166
+
167
+ Automatically save and restore player state across bot restarts.
168
+
169
+ ### Setup
170
+
171
+ ```ts
172
+ const manager = new PlayerManager({
173
+ plugins: [new YouTubePlugin()],
174
+ persistence: {
175
+ enabled: true,
176
+ provider: "file", // "file", "redis", or "database"
177
+ filePath: "./player_data",
178
+ saveInterval: 60000, // Save every minute
179
+ autoLoad: true, // Auto-load on startup
180
+ compress: true, // Compress saved data
181
+ maxBackups: 5, // Keep 5 backups
182
+ },
183
+ });
184
+
185
+ // Listen to persistence events
186
+ manager.on("playerSaved", (guildId) => console.log(`Saved ${guildId}`));
187
+ manager.on("playerLoaded", (guildId, data) => console.log(`Loaded ${guildId} from ${new Date(data.lastUpdate)}`));
188
+ ```
189
+
190
+ ### Manual Persistence
191
+
192
+ ```ts
193
+ // Save specific player
194
+ await manager.savePlayer(guildId);
195
+ await player.save(); // From player instance
196
+
197
+ // Save all players
198
+ await manager.saveAllPlayers();
199
+
200
+ // Load players
201
+ await manager.loadPlayer(guildId, true); // Restore playback position
202
+ await manager.loadAllPlayers();
203
+
204
+ // Delete saved data
205
+ const persistence = manager.getPersistence();
206
+ await persistence?.deletePlayer(guildId);
207
+ await persistence?.restoreBackup(guildId); // Restore from backup
208
+ ```
209
+
210
+ ---
211
+
212
+ ## 🔌 Plugins
213
+
214
+ Install via `@ziplayer/plugin`:
215
+
216
+ - **YouTubePlugin** — YouTube + search
217
+ - **SoundCloudPlugin** — SoundCloud streaming
218
+ - **SpotifyPlugin** — Metadata (uses fallback)
219
+ - **TTSPlugin** — Text-to-speech
220
+ - **AttachmentsPlugin** — Local/URL audio files
221
+
222
+ ### Example
223
+
224
+ ```ts
225
+ import { TTSPlugin } from "@ziplayer/plugin";
226
+
227
+ new PlayerManager({
228
+ plugins: [new TTSPlugin({ defaultLang: "en" })],
229
+ });
230
+ ```
231
+
232
+ ### Dynamic Plugin Registration
233
+
234
+ ```ts
235
+ // Register plugin after initialization
236
+ manager.registerPlugin(new YouTubePlugin());
237
+
238
+ // Get all registered plugins
239
+ const plugins = manager.getPlugins();
240
+ ```
241
+
242
+ ---
243
+
244
+ ## 🧩 Extensions
245
+
246
+ Enhance player behavior:
247
+
248
+ - 🎤 `voiceExt` — Speech-to-text commands
249
+ - 🎤 `lyricsExt` — Auto lyrics (synced support)
250
+ - ⚡ `lavalinkExt` — External Lavalink node
251
+
252
+ ### Example
253
+
254
+ ```ts
255
+ import { voiceExt, lyricsExt } from "@ziplayer/extension";
256
+
257
+ const manager = new PlayerManager({
258
+ extensions: [new voiceExt(null, { lang: "en-US" }), new lyricsExt(null, { provider: "lrclib" })],
259
+ });
260
+ ```
261
+
262
+ ### Extension Capabilities
263
+
264
+ Extensions can now provide:
265
+
266
+ - **Search** — Custom search handling
267
+ - **Stream** — Custom stream sources (Lavalink, etc.)
268
+ - **Before/After play hooks** — Modify playback behavior
269
+
270
+ ---
271
+
272
+ ## 🎛️ Audio Filters
273
+
274
+ Apply FFmpeg filters in real-time:
275
+
276
+ ```ts
277
+ await player.filter.applyFilter("bassboost");
278
+ await player.filter.applyFilter("nightcore");
279
+ await player.filter.applyFilters(["bassboost", "trebleboost"]); // Multiple filters
280
+ await player.filter.getFilterString(); // "bassboost,trebleboost"
281
+ await player.filter.clearAll();
282
+ ```
283
+
284
+ ### Available filters
285
+
286
+ - bassboost, trebleboost
287
+ - nightcore, lofi, vaporwave
288
+ - echo, reverb, chorus
289
+ - karaoke
290
+ - normalize, compressor, limiter
291
+
292
+ ---
293
+
294
+ ## 🔊 TTS (Interrupt Mode)
295
+
296
+ ```ts
297
+ const player = await manager.create(guildId, {
298
+ tts: {
299
+ createPlayer: true,
300
+ interrupt: true,
301
+ volume: 100,
302
+ maxTimeTts: 60000,
303
+ },
304
+ });
305
+
306
+ await player.play("tts: Hello everyone", userId);
307
+ ```
308
+
309
+ ---
310
+
311
+ ## 📡 Events
312
+
313
+ Listen globally via manager:
314
+
315
+ ```ts
316
+ manager.on("trackStart", (player, track) => {});
317
+ manager.on("trackEnd", (player, track) => {});
318
+ manager.on("queueEnd", (player) => {});
319
+ manager.on("playerError", (player, error, track) => {});
320
+ manager.on("playerPause", (player, track) => {});
321
+ manager.on("playerResume", (player, track) => {});
322
+ manager.on("volumeChange", (player, oldVolume, newVolume) => {});
323
+ manager.on("queueAdd", (player, track) => {});
324
+ manager.on("queueAddList", (player, tracks) => {});
325
+ manager.on("queueRemove", (player, track, index) => {});
326
+ manager.on("playerDestroy", (player) => {});
327
+ manager.on("ttsStart", (player, payload) => {});
328
+ manager.on("ttsEnd", (player) => {});
329
+
330
+ // Persistence events
331
+ manager.on("playerSaved", (guildId) => {});
332
+ manager.on("playerLoaded", (guildId, data) => {});
333
+ manager.on("savedAll", (results) => {});
334
+ manager.on("loadedAll", (results) => {});
335
+ ```
336
+
337
+ ---
338
+
339
+ ## 🧠 Advanced Features
340
+
341
+ ### Autoplay
342
+
343
+ ```ts
344
+ player.queue.autoPlay(true);
345
+ ```
346
+
347
+ ### Insert next track
348
+
349
+ ```ts
350
+ await player.insert("song", 0); // Insert at position 0 (play next)
351
+ await player.insert([track1, track2], 2); // Insert multiple at index 2
352
+ ```
353
+
354
+ ### Save stream to file
355
+
356
+ ```ts
357
+ const stream = await player.save(track);
358
+ stream.pipe(fs.createWriteStream("song.mp3"));
359
+
360
+ // Save with filters
361
+ const filteredStream = await player.save(track, {
362
+ filter: ["bassboost"],
363
+ seek: 30000, // Start from 30 seconds
364
+ });
365
+ ```
366
+
367
+ ### Progress Bar
368
+
369
+ ```ts
370
+ // Default (compact time format)
371
+ console.log(player.getProgressBar());
372
+ // Output: "1:22:12 ▬▬▬▬▬▬▬▬▬▬🔘▬▬▬▬▬▬▬▬ 1:45:30"
373
+
374
+ // Custom options
375
+ console.log(
376
+ player.getProgressBar({
377
+ size: 30,
378
+ barChar: "─",
379
+ progressChar: "●",
380
+ timeFormat: "full", // "full" or "compact"
381
+ showPercentage: true,
382
+ }),
383
+ );
384
+ // Output: "01:22:12 ───────●───────────────────── 01:45:30 (47%)"
385
+ ```
386
+
387
+ ### Time Formatting
388
+
389
+ ```ts
390
+ const time = player.getTime();
391
+ console.log(time.formatted.current); // "1:22:12" (compact)
392
+ console.log(time.format); // "01:22:12" (full with leading zeros)
393
+ ```
394
+
395
+ ### Batch Operations
396
+
397
+ ```ts
398
+ // Broadcast action to all players
399
+ manager.broadcast("setVolume", 50);
400
+ manager.broadcast("pause");
401
+
402
+ // Get players by filter
403
+ const activePlayers = manager.getPlayersByFilter((p) => p.isPlaying);
404
+
405
+ // Delete multiple players
406
+ manager.deleteWhere((p) => p.queue.isEmpty && !p.isPlaying);
407
+ ```
408
+
409
+ ---
410
+
411
+ ## ⚙️ Advanced Configuration
412
+
413
+ ### PlayerManager Options
414
+
415
+ ```ts
416
+ const manager = new PlayerManager({
417
+ plugins: [...],
418
+ extensions: [...],
419
+ extractorTimeout: 30000, // Timeout for stream extraction
420
+ autoCleanup: true, // Auto cleanup inactive players
421
+ cleanupInterval: 120000, // Cleanup interval (ms)
422
+ enableSearchCache: true, // Cache search results
423
+ enableStatsCollection: true, // Enable stats events
424
+ persistence: {...} // Persistence configuration
425
+ });
426
+ ```
427
+
428
+ ### Player Options
429
+
430
+ ```ts
431
+ const player = await manager.create(guildId, {
432
+ volume: 100,
433
+ quality: "high",
434
+ leaveOnEnd: true,
435
+ leaveOnEmpty: true,
436
+ leaveTimeout: 100000,
437
+ selfDeaf: true,
438
+ selfMute: false,
439
+ extractorTimeout: 50000,
440
+ filters: ["bassboost", "nightcore"],
441
+ tts: {
442
+ createPlayer: false,
443
+ interrupt: true,
444
+ volume: 100,
445
+ maxTimeTts: 60000,
446
+ },
447
+ userdata: { customField: "value" },
448
+ });
449
+ ```
450
+
451
+ ---
452
+
453
+ ## 📊 Monitoring & Stats
454
+
455
+ ```ts
456
+ // Get manager statistics
457
+ const stats = manager.getStats();
458
+ console.log({
459
+ totalPlayers: stats.totalPlayers,
460
+ activePlayers: stats.activePlayers,
461
+ pausedPlayers: stats.pausedPlayers,
462
+ connectedPlayers: stats.connectedPlayers,
463
+ totalTracksInQueue: stats.totalTracksInQueue,
464
+ });
465
+
466
+ // Get plugin/extension stats
467
+ console.log(manager.getConfig());
468
+ console.log(player.pluginManager.getStats());
469
+ console.log(player.extensionManager.getStats());
470
+
471
+ // Clear caches
472
+ player.clearSearchCache();
473
+ player.extensionManager.clearCache("search");
474
+ ```
475
+
476
+ ---
477
+
478
+ ## ⚠️ Best Practices
479
+
480
+ - Use **one PlayerManager** per bot
481
+ - Always `await player.connect()` before playing
482
+ - Handle `playerError` events
483
+ - Do not reuse a destroyed player
484
+ - Enable **persistence** for production bots to survive restarts
485
+ - Use **autoCleanup** to prevent memory leaks
486
+ - Set appropriate **extractorTimeout** based on your network (default: 10-50 seconds)
487
+
488
+ ---
489
+
490
+ ## 🌟 Migration Guide
491
+
492
+ ### From v1.x to v2.x
493
+
494
+ - `player.getTime()` now returns `{ current, total, format, formatted }`
495
+ - `player.getProgressBar()` supports new options
496
+ - `player.queue.remove(index)` removed track is now returned
497
+ - New `queue.removeMultiple()`, `queue.move()`, `queue.swap()` methods
498
+ - Extension hooks now support async properly
499
+
500
+ ---
501
+
502
+ ## 📚 Resources
503
+
504
+ - Examples: [https://github.com/ZiProject/ZiPlayer/tree/main/examples](https://github.com/ZiProject/ZiPlayer/tree/main/examples)
505
+ - GitHub: [https://github.com/ZiProject/ZiPlayer](https://github.com/ZiProject/ZiPlayer)
506
+ - npm: [https://www.npmjs.com/package/ziplayer](https://www.npmjs.com/package/ziplayer)
507
+
508
+ ---
509
+
510
+ ## 📄 License
511
+
512
+ MIT License
513
+ ````