ziplayer 0.0.8 → 0.1.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/README.md +61 -30
- package/dist/extensions/BaseExtension.d.ts +9 -3
- package/dist/extensions/BaseExtension.d.ts.map +1 -1
- package/dist/extensions/BaseExtension.js.map +1 -1
- package/dist/structures/Player.d.ts +299 -2
- package/dist/structures/Player.d.ts.map +1 -1
- package/dist/structures/Player.js +594 -86
- package/dist/structures/Player.js.map +1 -1
- package/dist/structures/PlayerManager.d.ts +166 -2
- package/dist/structures/PlayerManager.d.ts.map +1 -1
- package/dist/structures/PlayerManager.js +182 -8
- package/dist/structures/PlayerManager.js.map +1 -1
- package/dist/structures/Queue.d.ts +193 -2
- package/dist/structures/Queue.d.ts.map +1 -1
- package/dist/structures/Queue.js +193 -2
- package/dist/structures/Queue.js.map +1 -1
- package/dist/types/index.d.ts +327 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/timeout.d.ts +9 -0
- package/dist/utils/timeout.d.ts.map +1 -0
- package/dist/utils/timeout.js +14 -0
- package/dist/utils/timeout.js.map +1 -0
- package/package.json +1 -1
- package/src/extensions/BaseExtension.ts +35 -10
- package/src/structures/Player.ts +625 -88
- package/src/structures/PlayerManager.ts +189 -8
- package/src/structures/Queue.ts +196 -2
- package/src/types/index.ts +343 -4
- package/src/utils/timeout.ts +10 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
import { Player } from "./Player";
|
|
3
3
|
import { PlayerManagerOptions, PlayerOptions, Track, SourcePlugin, SearchResult } from "../types";
|
|
4
|
+
import type { BaseExtension } from "../extensions";
|
|
5
|
+
import { withTimeout } from "../utils/timeout";
|
|
4
6
|
|
|
5
7
|
const GLOBAL_MANAGER_KEY: symbol = Symbol.for("ziplayer.PlayerManager.instance");
|
|
6
8
|
export const getGlobalManager = (): PlayerManager | null => {
|
|
@@ -23,15 +25,49 @@ const setGlobalManager = (instance: PlayerManager): void => {
|
|
|
23
25
|
}
|
|
24
26
|
};
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The main class for managing players across multiple Discord guilds.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic setup with plugins and extensions
|
|
33
|
+
* const manager = new PlayerManager({
|
|
34
|
+
* plugins: [
|
|
35
|
+
* new YouTubePlugin(),
|
|
36
|
+
* new SoundCloudPlugin(),
|
|
37
|
+
* new SpotifyPlugin(),
|
|
38
|
+
* new TTSPlugin({ defaultLang: "en" })
|
|
39
|
+
* ],
|
|
40
|
+
* extensions: [
|
|
41
|
+
* new voiceExt(null, { lang: "en-US" }),
|
|
42
|
+
* new lavalinkExt(null, {
|
|
43
|
+
* nodes: [{ host: "localhost", port: 2333, password: "youshallnotpass" }]
|
|
44
|
+
* })
|
|
45
|
+
* ],
|
|
46
|
+
* extractorTimeout: 10000
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // Create a player for a guild
|
|
50
|
+
* const player = await manager.create(guildId, {
|
|
51
|
+
* tts: { interrupt: true, volume: 1 },
|
|
52
|
+
* leaveOnEnd: true,
|
|
53
|
+
* leaveTimeout: 30000
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* // Get existing player
|
|
57
|
+
* const existingPlayer = manager.get(guildId);
|
|
58
|
+
* if (existingPlayer) {
|
|
59
|
+
* await existingPlayer.play("Never Gonna Give You Up", userId);
|
|
60
|
+
* }
|
|
61
|
+
*/
|
|
26
62
|
export class PlayerManager extends EventEmitter {
|
|
27
63
|
private static instance: PlayerManager | null = null;
|
|
28
64
|
private players: Map<string, Player> = new Map();
|
|
29
|
-
static default(opt?: PlayerOptions): Player {
|
|
65
|
+
static async default(opt?: PlayerOptions): Promise<Player> {
|
|
30
66
|
let globaldef = getGlobalManager();
|
|
31
67
|
if (!globaldef) {
|
|
32
68
|
globaldef = new PlayerManager({});
|
|
33
69
|
}
|
|
34
|
-
return globaldef.create("default", opt);
|
|
70
|
+
return await globaldef.create("default", opt);
|
|
35
71
|
}
|
|
36
72
|
private plugins: SourcePlugin[];
|
|
37
73
|
private extensions: any[];
|
|
@@ -79,7 +115,42 @@ export class PlayerManager extends EventEmitter {
|
|
|
79
115
|
throw new Error("Invalid guild or guildId provided.");
|
|
80
116
|
}
|
|
81
117
|
|
|
82
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Create a new player for a guild
|
|
120
|
+
*
|
|
121
|
+
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
122
|
+
* @param {PlayerOptions} options - Player configuration options
|
|
123
|
+
* @returns {Promise<Player>} The created player instance
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // Create player with basic options
|
|
127
|
+
* const player = await manager.create(guildId, {
|
|
128
|
+
* tts: { interrupt: true, volume: 1 },
|
|
129
|
+
* leaveOnEnd: true,
|
|
130
|
+
* leaveTimeout: 30000
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* // Create player with advanced options
|
|
134
|
+
* const advancedPlayer = await manager.create(guild, {
|
|
135
|
+
* volume: 0.8,
|
|
136
|
+
* quality: "high",
|
|
137
|
+
* selfDeaf: false,
|
|
138
|
+
* selfMute: false,
|
|
139
|
+
* tts: {
|
|
140
|
+
* createPlayer: true,
|
|
141
|
+
* interrupt: true,
|
|
142
|
+
* volume: 1.0,
|
|
143
|
+
* Max_Time_TTS: 30000
|
|
144
|
+
* },
|
|
145
|
+
* userdata: { customData: "example" }
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* // Connect and play immediately
|
|
149
|
+
* await player.connect(voiceChannel);
|
|
150
|
+
* await player.play("Never Gonna Give You Up", userId);
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
async create(guildOrId: string | { id: string }, options?: PlayerOptions): Promise<Player> {
|
|
83
154
|
const guildId = this.resolveGuildId(guildOrId);
|
|
84
155
|
if (this.players.has(guildId)) {
|
|
85
156
|
return this.players.get(guildId)!;
|
|
@@ -116,14 +187,26 @@ export class PlayerManager extends EventEmitter {
|
|
|
116
187
|
}
|
|
117
188
|
}
|
|
118
189
|
if (instance && typeof instance === "object") {
|
|
119
|
-
|
|
120
|
-
if (
|
|
190
|
+
const extInstance = instance as BaseExtension;
|
|
191
|
+
if ("player" in extInstance && !extInstance.player) extInstance.player = player;
|
|
192
|
+
player.attachExtension(extInstance);
|
|
193
|
+
if (typeof extInstance.active === "function") {
|
|
194
|
+
let activated: boolean | void = true;
|
|
121
195
|
try {
|
|
122
|
-
|
|
123
|
-
|
|
196
|
+
activated = await withTimeout(
|
|
197
|
+
Promise.resolve(extInstance.active({ manager: this, player })),
|
|
198
|
+
player.options.extractorTimeout ?? 15000,
|
|
199
|
+
`Extension ${extInstance?.name} activation timed out`,
|
|
200
|
+
);
|
|
201
|
+
this.debug(`[PlayerManager] Extension ${extInstance?.name} active`);
|
|
124
202
|
} catch (e) {
|
|
203
|
+
activated = false;
|
|
125
204
|
this.debug(`[PlayerManager] Extension activation error:`, e);
|
|
126
205
|
}
|
|
206
|
+
if (activated === false) {
|
|
207
|
+
player.detachExtension(extInstance);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
127
210
|
}
|
|
128
211
|
}
|
|
129
212
|
}
|
|
@@ -158,15 +241,90 @@ export class PlayerManager extends EventEmitter {
|
|
|
158
241
|
return player;
|
|
159
242
|
}
|
|
160
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Get an existing player for a guild
|
|
246
|
+
*
|
|
247
|
+
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
248
|
+
* @returns {Player | undefined} The player instance or undefined if not found
|
|
249
|
+
* @example
|
|
250
|
+
* // Get player by guild ID
|
|
251
|
+
* const player = manager.get(guildId);
|
|
252
|
+
* if (player) {
|
|
253
|
+
* await player.play("Never Gonna Give You Up", userId);
|
|
254
|
+
* } else {
|
|
255
|
+
* console.log("No player found for this guild");
|
|
256
|
+
* }
|
|
257
|
+
*
|
|
258
|
+
* // Get player by guild object
|
|
259
|
+
* const playerFromGuild = manager.get(guild);
|
|
260
|
+
* if (playerFromGuild) {
|
|
261
|
+
* playerFromGuild.setVolume(0.5);
|
|
262
|
+
* }
|
|
263
|
+
*
|
|
264
|
+
* // Check if player exists before using
|
|
265
|
+
* const existingPlayer = manager.get(guildId);
|
|
266
|
+
* if (existingPlayer && existingPlayer.playing) {
|
|
267
|
+
* existingPlayer.pause();
|
|
268
|
+
* }
|
|
269
|
+
*/
|
|
270
|
+
|
|
161
271
|
get(guildOrId: string | { id: string }): Player | undefined {
|
|
162
272
|
const guildId = this.resolveGuildId(guildOrId);
|
|
163
273
|
return this.players.get(guildId);
|
|
164
274
|
}
|
|
165
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Get an existing player for a guild
|
|
278
|
+
*
|
|
279
|
+
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
280
|
+
* @returns {Player | undefined} The player instance or undefined
|
|
281
|
+
* @example
|
|
282
|
+
* const player = manager.get(guildId);
|
|
283
|
+
* if (player) {
|
|
284
|
+
* await player.play("song name", userId);
|
|
285
|
+
* }
|
|
286
|
+
*/
|
|
287
|
+
getPlayer(guildOrId: string | { id: string }): Player | undefined {
|
|
288
|
+
const guildId = this.resolveGuildId(guildOrId);
|
|
289
|
+
return this.players.get(guildId);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get all players
|
|
294
|
+
*
|
|
295
|
+
* @returns {Player[]} All player instances
|
|
296
|
+
* @example
|
|
297
|
+
* const players = manager.getall();
|
|
298
|
+
* console.log(`Players: ${players.length}`);
|
|
299
|
+
*/
|
|
166
300
|
getall(): Player[] | [] {
|
|
167
301
|
return Array.from(this.players.values());
|
|
168
302
|
}
|
|
169
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Destroy a player and clean up resources
|
|
306
|
+
*
|
|
307
|
+
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
308
|
+
* @returns {boolean} True if player was destroyed, false if not found
|
|
309
|
+
* @example
|
|
310
|
+
* // Destroy player by guild ID
|
|
311
|
+
* const destroyed = manager.delete(guildId);
|
|
312
|
+
* if (destroyed) {
|
|
313
|
+
* console.log("Player destroyed successfully");
|
|
314
|
+
* } else {
|
|
315
|
+
* console.log("No player found to destroy");
|
|
316
|
+
* }
|
|
317
|
+
*
|
|
318
|
+
* // Destroy player by guild object
|
|
319
|
+
* const destroyedFromGuild = manager.delete(guild);
|
|
320
|
+
* console.log(`Player destroyed: ${destroyedFromGuild}`);
|
|
321
|
+
*
|
|
322
|
+
* // Clean up all players
|
|
323
|
+
* for (const [guildId, player] of manager.players) {
|
|
324
|
+
* const destroyed = manager.delete(guildId);
|
|
325
|
+
* console.log(`Destroyed player for ${guildId}: ${destroyed}`);
|
|
326
|
+
* }
|
|
327
|
+
*/
|
|
170
328
|
delete(guildOrId: string | { id: string }): boolean {
|
|
171
329
|
const guildId = this.resolveGuildId(guildOrId);
|
|
172
330
|
const player = this.players.get(guildId);
|
|
@@ -178,6 +336,15 @@ export class PlayerManager extends EventEmitter {
|
|
|
178
336
|
return false;
|
|
179
337
|
}
|
|
180
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Check if a player exists for a guild
|
|
341
|
+
*
|
|
342
|
+
* @param {string | {id: string}} guildOrId - Guild ID or guild object
|
|
343
|
+
* @returns {boolean} True if player exists, false if not
|
|
344
|
+
* @example
|
|
345
|
+
* const exists = manager.has(guildId);
|
|
346
|
+
* console.log(`Player exists: ${exists}`);
|
|
347
|
+
*/
|
|
181
348
|
has(guildOrId: string | { id: string }): boolean {
|
|
182
349
|
const guildId = this.resolveGuildId(guildOrId);
|
|
183
350
|
return this.players.has(guildId);
|
|
@@ -190,7 +357,14 @@ export class PlayerManager extends EventEmitter {
|
|
|
190
357
|
get debugEnabled(): boolean {
|
|
191
358
|
return this.B_debug;
|
|
192
359
|
}
|
|
193
|
-
|
|
360
|
+
/**
|
|
361
|
+
* Destroy all players
|
|
362
|
+
*
|
|
363
|
+
* @returns {void}
|
|
364
|
+
* @example
|
|
365
|
+
* manager.destroy();
|
|
366
|
+
* console.log(`All players destroyed`);
|
|
367
|
+
*/
|
|
194
368
|
destroy(): void {
|
|
195
369
|
this.debug(`[PlayerManager] Destroying all players`);
|
|
196
370
|
for (const player of this.players.values()) {
|
|
@@ -202,6 +376,13 @@ export class PlayerManager extends EventEmitter {
|
|
|
202
376
|
|
|
203
377
|
/**
|
|
204
378
|
* Search using registered plugins without creating a Player.
|
|
379
|
+
*
|
|
380
|
+
* @param {string} query - The query to search for
|
|
381
|
+
* @param {string} requestedBy - The user ID who requested the search
|
|
382
|
+
* @returns {Promise<SearchResult>} The search result
|
|
383
|
+
* @example
|
|
384
|
+
* const result = await manager.search("Never Gonna Give You Up", userId);
|
|
385
|
+
* console.log(`Search result: ${result.tracks.length} tracks`);
|
|
205
386
|
*/
|
|
206
387
|
async search(query: string, requestedBy: string): Promise<SearchResult> {
|
|
207
388
|
this.debug(`[PlayerManager] Search called with query: ${query}, requestedBy: ${requestedBy}`);
|
package/src/structures/Queue.ts
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
import { Track, LoopMode } from "../types";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Manages the track queue for a player.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Basic queue operations
|
|
8
|
+
* const queue = player.queue;
|
|
9
|
+
*
|
|
10
|
+
* // Add single track
|
|
11
|
+
* queue.add(track);
|
|
12
|
+
*
|
|
13
|
+
* // Add multiple tracks
|
|
14
|
+
* queue.add([track1, track2, track3]);
|
|
15
|
+
*
|
|
16
|
+
* // Queue controls
|
|
17
|
+
* queue.shuffle(); // Randomize order
|
|
18
|
+
* queue.clear(); // Remove all tracks
|
|
19
|
+
* queue.autoPlay(true); // Enable auto-play
|
|
20
|
+
*
|
|
21
|
+
* // Get queue information
|
|
22
|
+
* console.log(`Queue length: ${queue.length}`);
|
|
23
|
+
* console.log(`Current track: ${queue.current?.title}`);
|
|
24
|
+
* console.log(`Is empty: ${queue.isEmpty}`);
|
|
25
|
+
* console.log(`Is playing: ${queue.isPlaying}`);
|
|
26
|
+
*
|
|
27
|
+
* // Loop modes
|
|
28
|
+
* queue.setLoopMode("track"); // Loop current track
|
|
29
|
+
* queue.setLoopMode("queue"); // Loop entire queue
|
|
30
|
+
* queue.setLoopMode("off"); // No loop
|
|
31
|
+
*
|
|
32
|
+
* // Remove specific track
|
|
33
|
+
* const removed = queue.remove(0); // Remove first track
|
|
34
|
+
* if (removed) {
|
|
35
|
+
* console.log(`Removed: ${removed.title}`);
|
|
36
|
+
* }
|
|
37
|
+
*/
|
|
3
38
|
export class Queue {
|
|
4
39
|
private tracks: Track[] = [];
|
|
5
40
|
private current: Track | null = null;
|
|
@@ -9,15 +44,37 @@ export class Queue {
|
|
|
9
44
|
private _loop: LoopMode = "off";
|
|
10
45
|
private willnext: Track | null = null;
|
|
11
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Add track(s) to the queue
|
|
49
|
+
*
|
|
50
|
+
* @param {Track | Track[]} track - Track or array of tracks to add
|
|
51
|
+
* @example
|
|
52
|
+
* queue.add(track);
|
|
53
|
+
* queue.add([track1, track2, track3]);
|
|
54
|
+
*/
|
|
12
55
|
add(track: Track): void {
|
|
13
56
|
this.tracks.push(track);
|
|
14
57
|
}
|
|
15
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Add multiple tracks to the queue
|
|
61
|
+
*
|
|
62
|
+
* @param {Track[]} tracks - Tracks to add
|
|
63
|
+
* @example
|
|
64
|
+
* queue.addMultiple([track1, track2, track3]);
|
|
65
|
+
*/
|
|
16
66
|
addMultiple(tracks: Track[]): void {
|
|
17
67
|
this.tracks.push(...tracks);
|
|
18
68
|
}
|
|
19
69
|
|
|
20
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Insert a track at a specific upcoming position (0 = next)
|
|
72
|
+
*
|
|
73
|
+
* @param {Track} track - Track to insert
|
|
74
|
+
* @param {number} index - Index to insert the track at
|
|
75
|
+
* @example
|
|
76
|
+
* queue.insert(track, 0);
|
|
77
|
+
*/
|
|
21
78
|
insert(track: Track, index: number): void {
|
|
22
79
|
if (!Number.isFinite(index)) {
|
|
23
80
|
this.tracks.push(track);
|
|
@@ -35,7 +92,14 @@ export class Queue {
|
|
|
35
92
|
this.tracks.splice(i, 0, track);
|
|
36
93
|
}
|
|
37
94
|
|
|
38
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Insert multiple tracks at a specific upcoming position, preserving order
|
|
97
|
+
*
|
|
98
|
+
* @param {Track[]} tracks - Tracks to insert
|
|
99
|
+
* @param {number} index - Index to insert the tracks at
|
|
100
|
+
* @example
|
|
101
|
+
* queue.insertMultiple([track1, track2, track3], 0);
|
|
102
|
+
*/
|
|
39
103
|
insertMultiple(tracks: Track[], index: number): void {
|
|
40
104
|
if (!Array.isArray(tracks) || tracks.length === 0) return;
|
|
41
105
|
if (!Number.isFinite(index)) {
|
|
@@ -54,11 +118,30 @@ export class Queue {
|
|
|
54
118
|
this.tracks.splice(i, 0, ...tracks);
|
|
55
119
|
}
|
|
56
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Remove a track from the queue
|
|
123
|
+
*
|
|
124
|
+
* @param {number} index - Index of track to remove
|
|
125
|
+
* @returns {Track | null} Removed track or null
|
|
126
|
+
* @example
|
|
127
|
+
* const removed = queue.remove(0);
|
|
128
|
+
* console.log(`Removed: ${removed?.title}`);
|
|
129
|
+
*/
|
|
130
|
+
|
|
57
131
|
remove(index: number): Track | null {
|
|
58
132
|
if (index < 0 || index >= this.tracks.length) return null;
|
|
59
133
|
return this.tracks.splice(index, 1)[0];
|
|
60
134
|
}
|
|
61
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Get the next track in the queue
|
|
138
|
+
*
|
|
139
|
+
* @param {boolean} ignoreLoop - Ignore the loop mode
|
|
140
|
+
* @returns {Track | null} The next track or null
|
|
141
|
+
* @example
|
|
142
|
+
* const nextTrack = queue.next();
|
|
143
|
+
* console.log(`Next track: ${nextTrack?.title}`);
|
|
144
|
+
*/
|
|
62
145
|
next(ignoreLoop = false): Track | null {
|
|
63
146
|
if (this.current) {
|
|
64
147
|
if (this._loop === "track" && !ignoreLoop) {
|
|
@@ -78,10 +161,26 @@ export class Queue {
|
|
|
78
161
|
return this.current;
|
|
79
162
|
}
|
|
80
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Clear all tracks from the queue
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* queue.clear();
|
|
169
|
+
*/
|
|
81
170
|
clear(): void {
|
|
82
171
|
this.tracks = [];
|
|
83
172
|
}
|
|
84
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Enable or disable auto-play
|
|
176
|
+
*
|
|
177
|
+
* @param {boolean} value - Enable/disable auto-play
|
|
178
|
+
* @returns {boolean} Current auto-play state
|
|
179
|
+
* @example
|
|
180
|
+
* queue.autoPlay(true);
|
|
181
|
+
* queue.autoPlay(); // Get current auto-play state
|
|
182
|
+
*/
|
|
183
|
+
|
|
85
184
|
autoPlay(value?: boolean): boolean {
|
|
86
185
|
if (typeof value !== "undefined") {
|
|
87
186
|
this._autoPlay = value;
|
|
@@ -89,6 +188,14 @@ export class Queue {
|
|
|
89
188
|
return this._autoPlay;
|
|
90
189
|
}
|
|
91
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Set the loop mode
|
|
193
|
+
*
|
|
194
|
+
* @param {LoopMode} mode - Loop mode to set
|
|
195
|
+
* @returns {LoopMode} The loop mode
|
|
196
|
+
* @example
|
|
197
|
+
* queue.loop("track");
|
|
198
|
+
*/
|
|
92
199
|
loop(mode?: LoopMode): LoopMode {
|
|
93
200
|
if (mode) {
|
|
94
201
|
this._loop = mode;
|
|
@@ -96,6 +203,13 @@ export class Queue {
|
|
|
96
203
|
return this._loop;
|
|
97
204
|
}
|
|
98
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Shuffle the queue
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* queue.shuffle();
|
|
211
|
+
*/
|
|
212
|
+
|
|
99
213
|
shuffle(): void {
|
|
100
214
|
for (let i = this.tracks.length - 1; i > 0; i--) {
|
|
101
215
|
const j = Math.floor(Math.random() * (i + 1));
|
|
@@ -103,22 +217,62 @@ export class Queue {
|
|
|
103
217
|
}
|
|
104
218
|
}
|
|
105
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Get the size of the queue
|
|
222
|
+
*
|
|
223
|
+
* @returns {number} The size of the queue
|
|
224
|
+
* @example
|
|
225
|
+
* const size = queue.size;
|
|
226
|
+
* console.log(`Queue size: ${size}`);
|
|
227
|
+
*/
|
|
106
228
|
get size(): number {
|
|
107
229
|
return this.tracks.length;
|
|
108
230
|
}
|
|
109
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Check if the queue is empty
|
|
234
|
+
*
|
|
235
|
+
* @returns {boolean} True if the queue is empty
|
|
236
|
+
* @example
|
|
237
|
+
* const empty = queue.isEmpty;
|
|
238
|
+
* console.log(`Queue is empty: ${empty}`);
|
|
239
|
+
*/
|
|
110
240
|
get isEmpty(): boolean {
|
|
111
241
|
return this.tracks.length === 0;
|
|
112
242
|
}
|
|
113
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Get the current track
|
|
246
|
+
*
|
|
247
|
+
* @returns {Track | null} The current track or null
|
|
248
|
+
* @example
|
|
249
|
+
* const currentTrack = queue.currentTrack;
|
|
250
|
+
* console.log(`Current track: ${currentTrack?.title}`);
|
|
251
|
+
*/
|
|
114
252
|
get currentTrack(): Track | null {
|
|
115
253
|
return this.current;
|
|
116
254
|
}
|
|
117
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Get the previous tracks
|
|
258
|
+
*
|
|
259
|
+
* @returns {Track[]} The previous tracks
|
|
260
|
+
* @example
|
|
261
|
+
* const previousTracks = queue.previousTracks;
|
|
262
|
+
* console.log(`Previous tracks: ${previousTracks.length}`);
|
|
263
|
+
*/
|
|
118
264
|
get previousTracks(): Track[] {
|
|
119
265
|
return [...this.history];
|
|
120
266
|
}
|
|
121
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Get the next track
|
|
270
|
+
*
|
|
271
|
+
* @returns {Track | null} The next track or null
|
|
272
|
+
* @example
|
|
273
|
+
* const nextTrack = queue.nextTrack;
|
|
274
|
+
* console.log(`Next track: ${nextTrack?.title}`);
|
|
275
|
+
*/
|
|
122
276
|
get nextTrack(): Track | null {
|
|
123
277
|
return this.tracks[0] || null;
|
|
124
278
|
}
|
|
@@ -126,6 +280,11 @@ export class Queue {
|
|
|
126
280
|
/**
|
|
127
281
|
* Move back to the previously played track.
|
|
128
282
|
* Makes the current track the next upcoming track, then sets previous as current.
|
|
283
|
+
*
|
|
284
|
+
* @returns {Track | null} The previous track or null
|
|
285
|
+
* @example
|
|
286
|
+
* const previousTrack = queue.previous();
|
|
287
|
+
* console.log(`Previous track: ${previousTrack?.title}`);
|
|
129
288
|
*/
|
|
130
289
|
previous(): Track | null {
|
|
131
290
|
if (this.history.length === 0) return null;
|
|
@@ -136,6 +295,15 @@ export class Queue {
|
|
|
136
295
|
return this.current;
|
|
137
296
|
}
|
|
138
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Get the next track
|
|
300
|
+
*
|
|
301
|
+
* @param {Track} track - The next track
|
|
302
|
+
* @returns {Track | null} The next track or null
|
|
303
|
+
* @example
|
|
304
|
+
* const nextTrack = queue.willNextTrack();
|
|
305
|
+
* console.log(`Next track: ${nextTrack?.title}`);
|
|
306
|
+
*/
|
|
139
307
|
willNextTrack(track?: Track): Track | null {
|
|
140
308
|
if (track) {
|
|
141
309
|
this.willnext = track;
|
|
@@ -143,6 +311,15 @@ export class Queue {
|
|
|
143
311
|
return this.willnext;
|
|
144
312
|
}
|
|
145
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Get the related tracks
|
|
316
|
+
*
|
|
317
|
+
* @param {Track[]} track - The related tracks
|
|
318
|
+
* @returns {Track[] | null} The related tracks or null
|
|
319
|
+
* @example
|
|
320
|
+
* const relatedTracks = queue.relatedTracks();
|
|
321
|
+
* console.log(`Related tracks: ${relatedTracks?.length}`);
|
|
322
|
+
*/
|
|
146
323
|
relatedTracks(track?: Track[]): Track[] | null {
|
|
147
324
|
if (track) {
|
|
148
325
|
this.related = track;
|
|
@@ -150,10 +327,27 @@ export class Queue {
|
|
|
150
327
|
return this.related;
|
|
151
328
|
}
|
|
152
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Get the tracks
|
|
332
|
+
*
|
|
333
|
+
* @returns {Track[]} The tracks
|
|
334
|
+
* @example
|
|
335
|
+
* const tracks = queue.getTracks();
|
|
336
|
+
* console.log(`Tracks: ${tracks.length}`);
|
|
337
|
+
*/
|
|
153
338
|
getTracks(): Track[] {
|
|
154
339
|
return [...this.tracks];
|
|
155
340
|
}
|
|
156
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Get a track at a specific index
|
|
344
|
+
*
|
|
345
|
+
* @param {number} index - The index of the track
|
|
346
|
+
* @returns {Track | null} The track or null
|
|
347
|
+
* @example
|
|
348
|
+
* const track = queue.getTrack(0);
|
|
349
|
+
* console.log(`Track: ${track?.title}`);
|
|
350
|
+
*/
|
|
157
351
|
getTrack(index: number): Track | null {
|
|
158
352
|
return this.tracks[index] || null;
|
|
159
353
|
}
|