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/AI-Guide.md ADDED
@@ -0,0 +1,607 @@
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
+ - Auto-save/restore (persistence)
27
+ - Smart caching and fallback system
28
+
29
+ ### Tech Stack
30
+
31
+ - TypeScript
32
+ - `@discordjs/voice` for audio
33
+ - FFmpeg for audio processing
34
+ - Node.js EventEmitter for events
35
+
36
+ ---
37
+
38
+ ## 🧱 Architecture
39
+
40
+ ```
41
+
42
+ ┌─────────────────────────────────────────────────────────────┐ │ PlayerManager (Global) │ │
43
+ ┌─────────────────────────────────────────────────────────┐│ │ │ PersistenceManager (Auto-save) ││ │
44
+ └─────────────────────────────────────────────────────────┘│ │ │ │ │ ┌────────────┴────────────┐ │ │ ▼ ▼ │ │ ┌──────────────┐
45
+ ┌──────────────┐ │ │ │ Player 1 │ │ Player 2 │ │ │ │ (Guild A) │ │ (Guild B) │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ │
46
+ ┌───────┴────────┐ ┌───────┴────────┐ │ │ ▼ ▼ ▼ ▼ │ │ Queue Filter Queue Filter │ │ PluginMgr ExtMgr PluginMgr ExtMgr │
47
+ └─────────────────────────────────────────────────────────────┘
48
+
49
+ ```
50
+
51
+ ### Component Responsibilities
52
+
53
+ | Component | Responsibility |
54
+ | -------------------- | ------------------------------------------------------ |
55
+ | `PlayerManager` | Creates/manages players, global event bus, persistence |
56
+ | `Player` | Per-guild audio playback, controls, event emission |
57
+ | `Queue` | Track management, loop modes, history, auto-play |
58
+ | `PluginManager` | Audio source resolution, streaming, fallback logic |
59
+ | `ExtensionManager` | Custom hooks (search, stream, before/after play) |
60
+ | `FilterManager` | FFmpeg audio effects |
61
+ | `PersistenceManager` | Auto-save/restore player state |
62
+
63
+ ---
64
+
65
+ ## 🧠 Core Concepts
66
+
67
+ ### 1. Player Lifecycle
68
+
69
+ ```typescript
70
+ // Create → Connect → Play → (Auto-save) → Destroy
71
+ const player = await manager.create(guildId, options);
72
+ await player.connect(voiceChannel);
73
+ await player.play(query, userId);
74
+ // ... auto-saves periodically
75
+ player.destroy();
76
+ ```
77
+
78
+ ### 2. Queue Loop Modes
79
+
80
+ ```typescript
81
+ player.loop("off"); // No loop (default)
82
+ player.loop("track"); // Repeat current track
83
+ player.loop("queue"); // Repeat entire queue
84
+ ```
85
+
86
+ ### 3. Event Flow
87
+
88
+ ```
89
+ trackStart → playing → trackEnd → playNext → (loop/autoplay)
90
+
91
+ queueEnd → leave
92
+ ```
93
+
94
+ ### 4. Plugin Priority & Fallback
95
+
96
+ ```typescript
97
+ // Plugins are tried in priority order (higher = first)
98
+ // If primary fails, fallback plugins are attempted sequentially
99
+ // Failed plugins don't block the queue
100
+
101
+ plugin.priority = 10; // Higher priority
102
+ ```
103
+
104
+ ### 5. Caching Strategy
105
+
106
+ | Cache Type | TTL | Purpose |
107
+ | --------------- | ------- | --------------------------- |
108
+ | Search cache | 2 min | Avoid duplicate API calls |
109
+ | Stream cache | 5 min | Cache resolved streams |
110
+ | Extension cache | 1-5 min | Extension operation results |
111
+
112
+ ---
113
+
114
+ ## 📚 API Reference
115
+
116
+ ### PlayerManager
117
+
118
+ #### Constructor Options
119
+
120
+ ```typescript
121
+ interface PlayerManagerOptions {
122
+ plugins?: SourcePlugin[]; // Audio source plugins
123
+ extensions?: BaseExtension[]; // Custom extensions
124
+ extractorTimeout?: number; // Default: 10000ms
125
+ autoCleanup?: boolean; // Default: true
126
+ cleanupInterval?: number; // Default: 60000ms
127
+ enableSearchCache?: boolean; // Default: true
128
+ enableStatsCollection?: boolean; // Default: false
129
+ persistence?: PersistenceOptions; // Auto-save config
130
+ }
131
+ ```
132
+
133
+ #### Key Methods
134
+
135
+ | Method | Description |
136
+ | ---------------------------- | -------------------------- |
137
+ | `create(guildId, options)` | Create new player |
138
+ | `get(guildId)` | Get existing player |
139
+ | `delete(guildId)` | Destroy and remove player |
140
+ | `getAll()` | Get all players |
141
+ | `broadcast(action, ...args)` | Send action to all players |
142
+ | `saveAllPlayers()` | Manual save all players |
143
+ | `loadAllPlayers()` | Manual load all players |
144
+
145
+ ### Player
146
+
147
+ #### Core Methods
148
+
149
+ | Method | Description | Returns |
150
+ | ------------------------------ | ----------------------- | ------------------- |
151
+ | `play(query, userId)` | Play track/search/queue | `Promise<boolean>` |
152
+ | `pause()` | Pause current | `boolean` |
153
+ | `resume()` | Resume playback | `boolean` |
154
+ | `skip(index?)` | Skip to next/index | `boolean` |
155
+ | `stop()` | Stop and clear queue | `boolean` |
156
+ | `seek(position)` | Seek to position (ms) | `Promise<boolean>` |
157
+ | `previous()` | Play previous track | `Promise<boolean>` |
158
+ | `setVolume(vol)` | Set volume (0-200) | `boolean` |
159
+ | `loop(mode)` | Set loop mode | `LoopMode` |
160
+ | `shuffle()` | Shuffle queue | `void` |
161
+ | `insert(query, index, userId)` | Insert at position | `Promise<boolean>` |
162
+ | `save(track, options)` | Save track to stream | `Promise<Readable>` |
163
+
164
+ #### Getters
165
+
166
+ ```typescript
167
+ player.currentTrack; // Track | null
168
+ player.queueSize; // number
169
+ player.isPlaying; // boolean
170
+ player.isPaused; // boolean
171
+ player.volume; // number
172
+ player.upcomingTracks; // Track[]
173
+ player.previousTracks; // Track[]
174
+ player.relatedTracks; // Track[] | null
175
+ ```
176
+
177
+ ### Queue
178
+
179
+ #### Methods
180
+
181
+ | Method | Description |
182
+ | ------------------------- | -------------------- |
183
+ | `add(track)` | Add single track |
184
+ | `addMultiple(tracks)` | Add multiple tracks |
185
+ | `insert(track, index)` | Insert at position |
186
+ | `remove(index)` | Remove at index |
187
+ | `removeMultiple(indices)` | Remove multiple |
188
+ | `removeWhere(predicate)` | Remove by condition |
189
+ | `move(from, to)` | Move track |
190
+ | `swap(a, b)` | Swap tracks |
191
+ | `shuffle()` | Randomize order |
192
+ | `clear()` | Clear all tracks |
193
+ | `loop(mode)` | Set loop mode |
194
+ | `autoPlay(enabled)` | Enable/disable |
195
+ | `previous()` | Get previous track |
196
+ | `jumpToHistory(steps)` | Jump back in history |
197
+
198
+ #### Properties
199
+
200
+ ```typescript
201
+ queue.size; // number
202
+ queue.isEmpty; // boolean
203
+ queue.currentTrack; // Track | null
204
+ queue.nextTrack; // Track | null
205
+ queue.lastTrack; // Track | null
206
+ queue.previousTracks; // Track[]
207
+ ```
208
+
209
+ ### FilterManager
210
+
211
+ ```typescript
212
+ // Apply filters
213
+ await player.filter.applyFilter("bassboost");
214
+ await player.filter.applyFilters(["bassboost", "nightcore"]);
215
+
216
+ // Available filters
217
+ // bassboost, trebleboost, nightcore, lofi, vaporwave,
218
+ // echo, reverb, chorus, karaoke, normalize, compressor, limiter
219
+
220
+ // Clear filters
221
+ await player.filter.clearAll();
222
+ await player.filter.clear("bassboost");
223
+
224
+ // Get current filters
225
+ const filterString = player.filter.getFilterString(); // "bassboost,nightcore"
226
+ ```
227
+
228
+ ### PersistenceManager
229
+
230
+ #### Options
231
+
232
+ ```typescript
233
+ interface PersistenceOptions {
234
+ enabled: boolean; // Enable persistence
235
+ provider: "file" | "redis" | "database";
236
+ filePath?: string; // For file provider
237
+ saveInterval?: number; // Auto-save interval (ms)
238
+ autoLoad?: boolean; // Auto-load on start
239
+ compress?: boolean; // Compress data
240
+ maxBackups?: number; // Max backup files
241
+ }
242
+ ```
243
+
244
+ #### Methods
245
+
246
+ ```typescript
247
+ const persistence = manager.getPersistence();
248
+
249
+ await persistence.savePlayer(player);
250
+ await persistence.saveAll();
251
+ await persistence.loadPlayer(guildId, restorePosition);
252
+ await persistence.loadAll();
253
+ await persistence.deletePlayer(guildId);
254
+ await persistence.restoreBackup(guildId, timestamp);
255
+ ```
256
+
257
+ ---
258
+
259
+ ## 🔧 Common Patterns
260
+
261
+ ### 1. Basic Music Bot Setup
262
+
263
+ ```typescript
264
+ import { Client, GatewayIntentBits } from "discord.js";
265
+ import { PlayerManager } from "ziplayer";
266
+ import { YouTubePlugin, SpotifyPlugin } from "@ziplayer/plugin";
267
+
268
+ const client = new Client({
269
+ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates],
270
+ });
271
+
272
+ const manager = new PlayerManager({
273
+ plugins: [new YouTubePlugin(), new SpotifyPlugin()],
274
+ autoCleanup: true,
275
+ persistence: {
276
+ enabled: true,
277
+ filePath: "./data/players",
278
+ },
279
+ });
280
+
281
+ client.on("ready", async () => {
282
+ // Auto-load saved players
283
+ await manager.loadAllPlayers();
284
+ });
285
+
286
+ client.on("messageCreate", async (msg) => {
287
+ if (msg.content.startsWith("!play")) {
288
+ const player = await manager.create(msg.guildId);
289
+ const voiceChannel = msg.member?.voice.channel;
290
+
291
+ if (!player.connection) {
292
+ await player.connect(voiceChannel);
293
+ }
294
+
295
+ const query = msg.content.slice(6);
296
+ await player.play(query, msg.author.id);
297
+ }
298
+ });
299
+ ```
300
+
301
+ ### 2. Progress Bar with Time Format
302
+
303
+ ```typescript
304
+ // Get formatted time
305
+ const time = player.getTime();
306
+ console.log(`Current: ${time.formatted.current}`); // "1:22:12"
307
+ console.log(`Total: ${time.formatted.total}`); // "3:45:30"
308
+
309
+ // Progress bar
310
+ const progressBar = player.getProgressBar({
311
+ size: 20,
312
+ barChar: "▬",
313
+ progressChar: "🔘",
314
+ timeFormat: "compact",
315
+ showPercentage: true,
316
+ });
317
+ // Output: "1:22:12 ▬▬▬▬▬▬▬▬▬🔘▬▬▬▬▬▬▬▬ 3:45:30 (36%)"
318
+ ```
319
+
320
+ ### 3. Queue Management Commands
321
+
322
+ ```typescript
323
+ // Skip to specific track
324
+ await player.skip(3); // Skip to index 3
325
+
326
+ // Move track to front
327
+ player.queue.move(5, 0);
328
+
329
+ // Remove all tracks from specific source
330
+ player.queue.removeWhere((t) => t.source === "soundcloud");
331
+
332
+ // Jump back 2 tracks
333
+ await player.queue.jumpToHistory(2);
334
+
335
+ // Insert as next track
336
+ await player.insert("song name", 0, userId);
337
+ ```
338
+
339
+ ### 4. Custom Plugin Implementation
340
+
341
+ ```typescript
342
+ import { BasePlugin, Track, StreamInfo } from "ziplayer";
343
+
344
+ class CustomPlugin extends BasePlugin {
345
+ name = "CustomPlugin";
346
+ priority = 5;
347
+
348
+ canHandle(query: string): boolean {
349
+ return query.startsWith("custom:");
350
+ }
351
+
352
+ async search(query: string, requestedBy: string): Promise<SearchResult> {
353
+ // Implementation
354
+ return { tracks: [] };
355
+ }
356
+
357
+ async getStream(track: Track, signal: AbortSignal): Promise<StreamInfo> {
358
+ // Return audio stream
359
+ return { stream: readableStream, type: "arbitrary" };
360
+ }
361
+
362
+ async getRelatedTracks(track: Track): Promise<Track[]> {
363
+ // Return recommendations
364
+ return [];
365
+ }
366
+ }
367
+ ```
368
+
369
+ ### 5. Custom Extension Implementation
370
+
371
+ ```typescript
372
+ import { BaseExtension, ExtensionContext } from "ziplayer";
373
+
374
+ class LoggerExtension extends BaseExtension {
375
+ name = "Logger";
376
+ priority = 10;
377
+
378
+ async beforePlay(context: ExtensionContext, request: any) {
379
+ console.log(`Playing: ${request.query}`);
380
+ return { handled: false };
381
+ }
382
+
383
+ async afterPlay(context: ExtensionContext, payload: any) {
384
+ if (payload.success) {
385
+ console.log(`Successfully played ${payload.tracks?.length} tracks`);
386
+ }
387
+ }
388
+ }
389
+ ```
390
+
391
+ ### 6. Event Handling
392
+
393
+ ```typescript
394
+ manager.on("trackStart", (player, track) => {
395
+ console.log(`Now playing: ${track.title}`);
396
+ });
397
+
398
+ manager.on("queueEnd", (player) => {
399
+ console.log("Queue finished!");
400
+ });
401
+
402
+ manager.on("playerError", (player, error, track) => {
403
+ console.error(`Error on ${track?.title}:`, error.message);
404
+ });
405
+
406
+ manager.on("playerSaved", (guildId) => {
407
+ console.log(`Saved state for guild ${guildId}`);
408
+ });
409
+
410
+ manager.on("stats", (stats) => {
411
+ console.log(`Active players: ${stats.activePlayers}`);
412
+ });
413
+ ```
414
+
415
+ ---
416
+
417
+ ## 🐛 Troubleshooting
418
+
419
+ ### Common Issues
420
+
421
+ | Issue | Solution |
422
+ | -------------------------- | ----------------------------------------------------------- |
423
+ | **No audio** | Check `player.connection` exists, voice channel permissions |
424
+ | **Plugin not working** | Verify `canHandle()` returns true, check priority |
425
+ | **Filters not applying** | Call `refreshPlayerResource(true)` after applying |
426
+ | **Persistence not saving** | Check `enabled: true`, file path writable |
427
+ | **Memory leak** | Enable `autoCleanup`, call `player.destroy()` when done |
428
+ | **Rate limiting** | Use search cache, increase `extractorTimeout` |
429
+
430
+ ### Debug Mode
431
+
432
+ ```typescript
433
+ // Enable debug logging
434
+ manager.on("debug", (message) => {
435
+ console.log("[DEBUG]", message);
436
+ });
437
+
438
+ // Or check debug flag
439
+ if (manager.debugEnabled) {
440
+ // Debug-specific logic
441
+ }
442
+ ```
443
+
444
+ ### Performance Tips
445
+
446
+ 1. **Enable caching** for search and stream results
447
+ 2. **Use persistence** to avoid re-fetching on restart
448
+ 3. **Set appropriate timeouts** based on network conditions
449
+ 4. **Batch operations** when modifying queue
450
+ 5. **Destroy players** when no longer needed
451
+
452
+ ---
453
+
454
+ ## 📝 Code Examples
455
+
456
+ ### Full Bot Example
457
+
458
+ ```typescript
459
+ import { Client, GatewayIntentBits, EmbedBuilder } from "discord.js";
460
+ import { PlayerManager } from "ziplayer";
461
+ import { YouTubePlugin, SpotifyPlugin, TTSPlugin } from "@ziplayer/plugin";
462
+
463
+ const client = new Client({
464
+ intents: [
465
+ GatewayIntentBits.Guilds,
466
+ GatewayIntentBits.GuildVoiceStates,
467
+ GatewayIntentBits.GuildMessages,
468
+ GatewayIntentBits.MessageContent,
469
+ ],
470
+ });
471
+
472
+ const manager = new PlayerManager({
473
+ plugins: [new YouTubePlugin(), new SpotifyPlugin(), new TTSPlugin()],
474
+ autoCleanup: true,
475
+ extractorTimeout: 30000,
476
+ persistence: {
477
+ enabled: true,
478
+ filePath: "./player_data",
479
+ saveInterval: 60000,
480
+ autoLoad: true,
481
+ },
482
+ });
483
+
484
+ client.on("messageCreate", async (msg) => {
485
+ if (!msg.guildId || msg.author.bot) return;
486
+
487
+ const args = msg.content.slice(1).split(" ");
488
+ const command = args[0].toLowerCase();
489
+ const query = args.slice(1).join(" ");
490
+
491
+ const player = await manager.create(msg.guildId);
492
+ const voiceChannel = msg.member?.voice.channel;
493
+
494
+ switch (command) {
495
+ case "play":
496
+ if (!voiceChannel) return msg.reply("Join a voice channel!");
497
+ if (!player.connection) await player.connect(voiceChannel);
498
+ await player.play(query, msg.author.id);
499
+ break;
500
+
501
+ case "pause":
502
+ player.pause();
503
+ break;
504
+
505
+ case "resume":
506
+ player.resume();
507
+ break;
508
+
509
+ case "skip":
510
+ player.skip();
511
+ break;
512
+
513
+ case "stop":
514
+ player.stop();
515
+ break;
516
+
517
+ case "volume":
518
+ const vol = parseInt(query);
519
+ if (isNaN(vol)) return msg.reply("Volume must be a number!");
520
+ player.setVolume(vol);
521
+ break;
522
+
523
+ case "queue":
524
+ const tracks = player.upcomingTracks.slice(0, 10);
525
+ const embed = new EmbedBuilder()
526
+ .setTitle("Queue")
527
+ .setDescription(tracks.map((t, i) => `${i + 1}. ${t.title}`).join("\n") || "Empty");
528
+ msg.reply({ embeds: [embed] });
529
+ break;
530
+
531
+ case "nowplaying":
532
+ const track = player.currentTrack;
533
+ if (!track) return msg.reply("Nothing playing!");
534
+
535
+ const progress = player.getProgressBar({ size: 15 });
536
+ const time = player.getTime();
537
+
538
+ const embed = new EmbedBuilder()
539
+ .setTitle(track.title)
540
+ .setURL(track.url)
541
+ .setThumbnail(track.thumbnail)
542
+ .setDescription(`\`${progress}\`\n${time.formatted.current} / ${time.formatted.total}`);
543
+ msg.reply({ embeds: [embed] });
544
+ break;
545
+ }
546
+ });
547
+
548
+ client.login(process.env.DISCORD_TOKEN);
549
+ ```
550
+
551
+ ---
552
+
553
+ ## 🔗 Quick Reference
554
+
555
+ ### Import Paths
556
+
557
+ ```typescript
558
+ // Core
559
+ import { PlayerManager, Player, Queue } from "ziplayer";
560
+
561
+ // Types
562
+ import type { Track, SearchResult, LoopMode, StreamInfo } from "ziplayer";
563
+
564
+ // Plugins (external package)
565
+ import { YouTubePlugin, SpotifyPlugin, TTSPlugin } from "@ziplayer/plugin";
566
+
567
+ // infinity plugin support stream audio from YouTube, TikTok, Instagram, Twitter/X, SoundCloud, Reddit, Twitch, Bilibili, and 1000+ other sites
568
+
569
+ import { InfinityPlugin } from "@ziplayer/infinity";
570
+
571
+ // Extensions (external package)
572
+ import { voiceExt, lyricsExt, lavalinkExt } from "@ziplayer/extension";
573
+ ```
574
+
575
+ ### Type Definitions
576
+
577
+ ```typescript
578
+ interface Track {
579
+ id: string;
580
+ title: string;
581
+ url: string;
582
+ source: string;
583
+ duration: number;
584
+ thumbnail?: string;
585
+ requestedBy?: string;
586
+ isLive?: boolean;
587
+ }
588
+
589
+ type LoopMode = "off" | "track" | "queue";
590
+
591
+ interface SearchResult {
592
+ tracks: Track[];
593
+ playlist?: { name: string; url?: string };
594
+ }
595
+ ```
596
+
597
+ ---
598
+
599
+ ## 📖 Additional Resources
600
+
601
+ - [GitHub Repository](https://github.com/ZiProject/ZiPlayer)
602
+ - [npm Package](https://www.npmjs.com/package/ziplayer)
603
+ - [Examples Folder](https://github.com/ZiProject/ZiPlayer/tree/main/examples)
604
+
605
+ ---
606
+
607
+ _This guide is maintained for AI assistants and developers. For questions or contributions, please open an issue on GitHub._