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/src/structures/Queue.ts
CHANGED
|
@@ -44,27 +44,41 @@ export class Queue {
|
|
|
44
44
|
private _loop: LoopMode = "off";
|
|
45
45
|
private willnext: Track | null = null;
|
|
46
46
|
|
|
47
|
+
// Configuration
|
|
48
|
+
private readonly MAX_HISTORY_SIZE = 200;
|
|
49
|
+
private readonly MAX_QUEUE_SIZE = 1000; // Prevent memory issues
|
|
50
|
+
|
|
47
51
|
/**
|
|
48
52
|
* Add track(s) to the queue
|
|
49
53
|
*
|
|
50
54
|
* @param {Track | Track[]} track - Track or array of tracks to add
|
|
55
|
+
* @returns {number} New queue size
|
|
51
56
|
* @example
|
|
52
57
|
* queue.add(track);
|
|
53
58
|
* queue.add([track1, track2, track3]);
|
|
54
59
|
*/
|
|
55
|
-
add(track: Track):
|
|
60
|
+
add(track: Track): number {
|
|
61
|
+
if (this.tracks.length >= this.MAX_QUEUE_SIZE) {
|
|
62
|
+
throw new Error(`Queue size limit reached (${this.MAX_QUEUE_SIZE})`);
|
|
63
|
+
}
|
|
56
64
|
this.tracks.push(track);
|
|
65
|
+
return this.tracks.length;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
/**
|
|
60
69
|
* Add multiple tracks to the queue
|
|
61
70
|
*
|
|
62
71
|
* @param {Track[]} tracks - Tracks to add
|
|
72
|
+
* @returns {number} New queue size
|
|
63
73
|
* @example
|
|
64
74
|
* queue.addMultiple([track1, track2, track3]);
|
|
65
75
|
*/
|
|
66
|
-
addMultiple(tracks: Track[]):
|
|
76
|
+
addMultiple(tracks: Track[]): number {
|
|
77
|
+
if (this.tracks.length + tracks.length > this.MAX_QUEUE_SIZE) {
|
|
78
|
+
throw new Error(`Adding ${tracks.length} tracks would exceed queue size limit (${this.MAX_QUEUE_SIZE})`);
|
|
79
|
+
}
|
|
67
80
|
this.tracks.push(...tracks);
|
|
81
|
+
return this.tracks.length;
|
|
68
82
|
}
|
|
69
83
|
|
|
70
84
|
/**
|
|
@@ -72,24 +86,31 @@ export class Queue {
|
|
|
72
86
|
*
|
|
73
87
|
* @param {Track} track - Track to insert
|
|
74
88
|
* @param {number} index - Index to insert the track at
|
|
89
|
+
* @returns {number} New queue size
|
|
75
90
|
* @example
|
|
76
91
|
* queue.insert(track, 0);
|
|
77
92
|
*/
|
|
78
|
-
insert(track: Track, index: number):
|
|
93
|
+
insert(track: Track, index: number): number {
|
|
94
|
+
if (this.tracks.length >= this.MAX_QUEUE_SIZE) {
|
|
95
|
+
throw new Error(`Queue size limit reached (${this.MAX_QUEUE_SIZE})`);
|
|
96
|
+
}
|
|
97
|
+
|
|
79
98
|
if (!Number.isFinite(index)) {
|
|
80
99
|
this.tracks.push(track);
|
|
81
|
-
return;
|
|
100
|
+
return this.tracks.length;
|
|
82
101
|
}
|
|
102
|
+
|
|
83
103
|
const i = Math.max(0, Math.min(Math.floor(index), this.tracks.length));
|
|
104
|
+
|
|
84
105
|
if (i === this.tracks.length) {
|
|
85
106
|
this.tracks.push(track);
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
if (i <= 0) {
|
|
107
|
+
} else if (i <= 0) {
|
|
89
108
|
this.tracks.unshift(track);
|
|
90
|
-
|
|
109
|
+
} else {
|
|
110
|
+
this.tracks.splice(i, 0, track);
|
|
91
111
|
}
|
|
92
|
-
|
|
112
|
+
|
|
113
|
+
return this.tracks.length;
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
/**
|
|
@@ -97,25 +118,33 @@ export class Queue {
|
|
|
97
118
|
*
|
|
98
119
|
* @param {Track[]} tracks - Tracks to insert
|
|
99
120
|
* @param {number} index - Index to insert the tracks at
|
|
121
|
+
* @returns {number} New queue size
|
|
100
122
|
* @example
|
|
101
123
|
* queue.insertMultiple([track1, track2, track3], 0);
|
|
102
124
|
*/
|
|
103
|
-
insertMultiple(tracks: Track[], index: number):
|
|
104
|
-
if (!Array.isArray(tracks) || tracks.length === 0) return;
|
|
125
|
+
insertMultiple(tracks: Track[], index: number): number {
|
|
126
|
+
if (!Array.isArray(tracks) || tracks.length === 0) return this.tracks.length;
|
|
127
|
+
|
|
128
|
+
if (this.tracks.length + tracks.length > this.MAX_QUEUE_SIZE) {
|
|
129
|
+
throw new Error(`Inserting ${tracks.length} tracks would exceed queue size limit (${this.MAX_QUEUE_SIZE})`);
|
|
130
|
+
}
|
|
131
|
+
|
|
105
132
|
if (!Number.isFinite(index)) {
|
|
106
133
|
this.tracks.push(...tracks);
|
|
107
|
-
return;
|
|
134
|
+
return this.tracks.length;
|
|
108
135
|
}
|
|
136
|
+
|
|
109
137
|
const i = Math.max(0, Math.min(Math.floor(index), this.tracks.length));
|
|
138
|
+
|
|
110
139
|
if (i === 0) {
|
|
111
140
|
this.tracks = [...tracks, ...this.tracks];
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
if (i === this.tracks.length) {
|
|
141
|
+
} else if (i === this.tracks.length) {
|
|
115
142
|
this.tracks.push(...tracks);
|
|
116
|
-
|
|
143
|
+
} else {
|
|
144
|
+
this.tracks.splice(i, 0, ...tracks);
|
|
117
145
|
}
|
|
118
|
-
|
|
146
|
+
|
|
147
|
+
return this.tracks.length;
|
|
119
148
|
}
|
|
120
149
|
|
|
121
150
|
/**
|
|
@@ -127,10 +156,49 @@ export class Queue {
|
|
|
127
156
|
* const removed = queue.remove(0);
|
|
128
157
|
* console.log(`Removed: ${removed?.title}`);
|
|
129
158
|
*/
|
|
130
|
-
|
|
131
159
|
remove(index: number): Track | null {
|
|
132
160
|
if (index < 0 || index >= this.tracks.length) return null;
|
|
133
|
-
|
|
161
|
+
const removed = this.tracks.splice(index, 1)[0];
|
|
162
|
+
return removed;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Remove multiple tracks by indices
|
|
167
|
+
*
|
|
168
|
+
* @param {number[]} indices - Array of indices to remove
|
|
169
|
+
* @returns {Track[]} Removed tracks
|
|
170
|
+
* @example
|
|
171
|
+
* const removed = queue.removeMultiple([0, 2, 5]);
|
|
172
|
+
*/
|
|
173
|
+
removeMultiple(indices: number[]): Track[] {
|
|
174
|
+
const sorted = [...new Set(indices)].sort((a, b) => b - a);
|
|
175
|
+
const removed: Track[] = [];
|
|
176
|
+
|
|
177
|
+
for (const index of sorted) {
|
|
178
|
+
if (index >= 0 && index < this.tracks.length) {
|
|
179
|
+
removed.unshift(this.tracks.splice(index, 1)[0]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return removed;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Remove tracks by predicate
|
|
188
|
+
*
|
|
189
|
+
* @param {(track: Track, index: number) => boolean} predicate - Filter function
|
|
190
|
+
* @returns {Track[]} Removed tracks
|
|
191
|
+
* @example
|
|
192
|
+
* const removed = queue.removeWhere(track => track.source === "youtube");
|
|
193
|
+
*/
|
|
194
|
+
removeWhere(predicate: (track: Track, index: number) => boolean): Track[] {
|
|
195
|
+
const removed: Track[] = [];
|
|
196
|
+
for (let i = this.tracks.length - 1; i >= 0; i--) {
|
|
197
|
+
if (predicate(this.tracks[i], i)) {
|
|
198
|
+
removed.unshift(this.tracks.splice(i, 1)[0]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return removed;
|
|
134
202
|
}
|
|
135
203
|
|
|
136
204
|
/**
|
|
@@ -143,24 +211,39 @@ export class Queue {
|
|
|
143
211
|
* console.log(`Next track: ${nextTrack?.title}`);
|
|
144
212
|
*/
|
|
145
213
|
next(ignoreLoop = false): Track | null {
|
|
214
|
+
// Handle track loop
|
|
215
|
+
if (this.current && this._loop === "track" && !ignoreLoop) {
|
|
216
|
+
return this.current;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Save current to history before moving to next
|
|
146
220
|
if (this.current) {
|
|
147
|
-
|
|
148
|
-
return this.current;
|
|
149
|
-
}
|
|
150
|
-
this.history.push(this.current);
|
|
151
|
-
if (this.history.length > 200) {
|
|
152
|
-
this.history.shift();
|
|
153
|
-
}
|
|
221
|
+
this.addToHistory(this.current);
|
|
154
222
|
}
|
|
223
|
+
|
|
224
|
+
// Get next track
|
|
155
225
|
this.current = this.tracks.shift() || null;
|
|
226
|
+
|
|
227
|
+
// Handle queue loop
|
|
156
228
|
if (!this.current && this._loop === "queue" && this.history.length > 0 && !ignoreLoop) {
|
|
157
229
|
this.tracks = [...this.history];
|
|
158
230
|
this.history = [];
|
|
159
231
|
this.current = this.tracks.shift() || null;
|
|
160
232
|
}
|
|
233
|
+
|
|
161
234
|
return this.current;
|
|
162
235
|
}
|
|
163
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Add track to history with size limit
|
|
239
|
+
*/
|
|
240
|
+
private addToHistory(track: Track): void {
|
|
241
|
+
this.history.push(track);
|
|
242
|
+
if (this.history.length > this.MAX_HISTORY_SIZE) {
|
|
243
|
+
this.history.shift();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
164
247
|
/**
|
|
165
248
|
* Clear all tracks from the queue
|
|
166
249
|
*
|
|
@@ -169,6 +252,31 @@ export class Queue {
|
|
|
169
252
|
*/
|
|
170
253
|
clear(): void {
|
|
171
254
|
this.tracks = [];
|
|
255
|
+
// Optionally reset current track? Usually not, but provide option
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Clear history
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* queue.clearHistory();
|
|
263
|
+
*/
|
|
264
|
+
clearHistory(): void {
|
|
265
|
+
this.history = [];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Reset entire queue (current, history, tracks)
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* queue.reset();
|
|
273
|
+
*/
|
|
274
|
+
reset(): void {
|
|
275
|
+
this.tracks = [];
|
|
276
|
+
this.current = null;
|
|
277
|
+
this.history = [];
|
|
278
|
+
this.related = [];
|
|
279
|
+
this.willnext = null;
|
|
172
280
|
}
|
|
173
281
|
|
|
174
282
|
/**
|
|
@@ -180,7 +288,6 @@ export class Queue {
|
|
|
180
288
|
* queue.autoPlay(true);
|
|
181
289
|
* queue.autoPlay(); // Get current auto-play state
|
|
182
290
|
*/
|
|
183
|
-
|
|
184
291
|
autoPlay(value?: boolean): boolean {
|
|
185
292
|
if (typeof value !== "undefined") {
|
|
186
293
|
this._autoPlay = value;
|
|
@@ -203,14 +310,32 @@ export class Queue {
|
|
|
203
310
|
return this._loop;
|
|
204
311
|
}
|
|
205
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Check if queue is currently looping
|
|
315
|
+
*
|
|
316
|
+
* @returns {boolean} True if looping
|
|
317
|
+
*/
|
|
318
|
+
isLooping(): boolean {
|
|
319
|
+
return this._loop !== "off";
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get current loop mode
|
|
324
|
+
*
|
|
325
|
+
* @returns {LoopMode} Current loop mode
|
|
326
|
+
*/
|
|
327
|
+
getLoopMode(): LoopMode {
|
|
328
|
+
return this._loop;
|
|
329
|
+
}
|
|
330
|
+
|
|
206
331
|
/**
|
|
207
332
|
* Shuffle the queue
|
|
208
333
|
*
|
|
209
334
|
* @example
|
|
210
335
|
* queue.shuffle();
|
|
211
336
|
*/
|
|
212
|
-
|
|
213
337
|
shuffle(): void {
|
|
338
|
+
// Fisher-Yates shuffle
|
|
214
339
|
for (let i = this.tracks.length - 1; i > 0; i--) {
|
|
215
340
|
const j = Math.floor(Math.random() * (i + 1));
|
|
216
341
|
[this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
|
|
@@ -218,12 +343,44 @@ export class Queue {
|
|
|
218
343
|
}
|
|
219
344
|
|
|
220
345
|
/**
|
|
221
|
-
*
|
|
346
|
+
* Move a track from one position to another
|
|
347
|
+
*
|
|
348
|
+
* @param {number} fromIndex - Current index
|
|
349
|
+
* @param {number} toIndex - Target index
|
|
350
|
+
* @returns {boolean} True if move was successful
|
|
351
|
+
* @example
|
|
352
|
+
* queue.move(3, 0); // Move track at index 3 to position 0
|
|
353
|
+
*/
|
|
354
|
+
move(fromIndex: number, toIndex: number): boolean {
|
|
355
|
+
if (fromIndex < 0 || fromIndex >= this.tracks.length) return false;
|
|
356
|
+
if (toIndex < 0 || toIndex >= this.tracks.length) return false;
|
|
357
|
+
if (fromIndex === toIndex) return true;
|
|
358
|
+
|
|
359
|
+
const [track] = this.tracks.splice(fromIndex, 1);
|
|
360
|
+
this.tracks.splice(toIndex, 0, track);
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Swap two tracks in the queue
|
|
222
366
|
*
|
|
223
|
-
* @
|
|
367
|
+
* @param {number} indexA - First track index
|
|
368
|
+
* @param {number} indexB - Second track index
|
|
369
|
+
* @returns {boolean} True if swap was successful
|
|
224
370
|
* @example
|
|
225
|
-
*
|
|
226
|
-
|
|
371
|
+
* queue.swap(0, 3);
|
|
372
|
+
*/
|
|
373
|
+
swap(indexA: number, indexB: number): boolean {
|
|
374
|
+
if (indexA < 0 || indexA >= this.tracks.length) return false;
|
|
375
|
+
if (indexB < 0 || indexB >= this.tracks.length) return false;
|
|
376
|
+
if (indexA === indexB) return true;
|
|
377
|
+
|
|
378
|
+
[this.tracks[indexA], this.tracks[indexB]] = [this.tracks[indexB], this.tracks[indexA]];
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get the size of the queue
|
|
227
384
|
*/
|
|
228
385
|
get size(): number {
|
|
229
386
|
return this.tracks.length;
|
|
@@ -231,11 +388,6 @@ export class Queue {
|
|
|
231
388
|
|
|
232
389
|
/**
|
|
233
390
|
* 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
391
|
*/
|
|
240
392
|
get isEmpty(): boolean {
|
|
241
393
|
return this.tracks.length === 0;
|
|
@@ -243,11 +395,6 @@ export class Queue {
|
|
|
243
395
|
|
|
244
396
|
/**
|
|
245
397
|
* 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
398
|
*/
|
|
252
399
|
get currentTrack(): Track | null {
|
|
253
400
|
return this.current;
|
|
@@ -255,28 +402,32 @@ export class Queue {
|
|
|
255
402
|
|
|
256
403
|
/**
|
|
257
404
|
* 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
405
|
*/
|
|
264
406
|
get previousTracks(): Track[] {
|
|
265
407
|
return [...this.history];
|
|
266
408
|
}
|
|
267
409
|
|
|
410
|
+
/**
|
|
411
|
+
* Get the number of previous tracks
|
|
412
|
+
*/
|
|
413
|
+
get previousTracksCount(): number {
|
|
414
|
+
return this.history.length;
|
|
415
|
+
}
|
|
416
|
+
|
|
268
417
|
/**
|
|
269
418
|
* 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
419
|
*/
|
|
276
420
|
get nextTrack(): Track | null {
|
|
277
421
|
return this.tracks[0] || null;
|
|
278
422
|
}
|
|
279
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Get the last track in the queue
|
|
426
|
+
*/
|
|
427
|
+
get lastTrack(): Track | null {
|
|
428
|
+
return this.tracks[this.tracks.length - 1] || null;
|
|
429
|
+
}
|
|
430
|
+
|
|
280
431
|
/**
|
|
281
432
|
* Move back to the previously played track.
|
|
282
433
|
* Makes the current track the next upcoming track, then sets previous as current.
|
|
@@ -288,15 +439,48 @@ export class Queue {
|
|
|
288
439
|
*/
|
|
289
440
|
previous(): Track | null {
|
|
290
441
|
if (this.history.length === 0) return null;
|
|
442
|
+
|
|
291
443
|
if (this.current) {
|
|
292
444
|
this.tracks.unshift(this.current);
|
|
293
445
|
}
|
|
446
|
+
|
|
294
447
|
this.current = this.history.pop() || null;
|
|
295
448
|
return this.current;
|
|
296
449
|
}
|
|
297
450
|
|
|
298
451
|
/**
|
|
299
|
-
*
|
|
452
|
+
* Jump to a specific track in history
|
|
453
|
+
*
|
|
454
|
+
* @param {number} stepsBack - Number of steps back in history (1 = previous)
|
|
455
|
+
* @returns {Track | null} The jumped-to track or null
|
|
456
|
+
* @example
|
|
457
|
+
* queue.jumpToHistory(2); // Go back 2 tracks
|
|
458
|
+
*/
|
|
459
|
+
jumpToHistory(stepsBack: number): Track | null {
|
|
460
|
+
if (stepsBack <= 0 || stepsBack > this.history.length) return null;
|
|
461
|
+
|
|
462
|
+
const targetIndex = this.history.length - stepsBack;
|
|
463
|
+
if (targetIndex < 0) return null;
|
|
464
|
+
|
|
465
|
+
// Save current track to queue if exists
|
|
466
|
+
if (this.current) {
|
|
467
|
+
this.tracks.unshift(this.current);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Get tracks after target to push back to queue
|
|
471
|
+
const tracksAfterTarget = this.history.splice(targetIndex + 1);
|
|
472
|
+
this.current = this.history.pop() || null;
|
|
473
|
+
|
|
474
|
+
// Push tracks after target back to queue (in reverse order to maintain sequence)
|
|
475
|
+
for (let i = tracksAfterTarget.length - 1; i >= 0; i--) {
|
|
476
|
+
this.tracks.unshift(tracksAfterTarget[i]);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return this.current;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Get the next track (for auto-play)
|
|
300
484
|
*
|
|
301
485
|
* @param {Track} track - The next track
|
|
302
486
|
* @returns {Track | null} The next track or null
|
|
@@ -328,9 +512,9 @@ export class Queue {
|
|
|
328
512
|
}
|
|
329
513
|
|
|
330
514
|
/**
|
|
331
|
-
* Get the
|
|
515
|
+
* Get all tracks in the queue
|
|
332
516
|
*
|
|
333
|
-
* @returns {Track[]}
|
|
517
|
+
* @returns {Track[]} Copy of tracks array
|
|
334
518
|
* @example
|
|
335
519
|
* const tracks = queue.getTracks();
|
|
336
520
|
* console.log(`Tracks: ${tracks.length}`);
|
|
@@ -339,6 +523,30 @@ export class Queue {
|
|
|
339
523
|
return [...this.tracks];
|
|
340
524
|
}
|
|
341
525
|
|
|
526
|
+
/**
|
|
527
|
+
* Get serializable queue data
|
|
528
|
+
*/
|
|
529
|
+
toJSON(): object {
|
|
530
|
+
return {
|
|
531
|
+
tracks: this.tracks,
|
|
532
|
+
current: this.current,
|
|
533
|
+
history: this.history,
|
|
534
|
+
size: this.size,
|
|
535
|
+
loopMode: this._loop,
|
|
536
|
+
autoPlay: this._autoPlay,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Restore queue from serialized data
|
|
542
|
+
*/
|
|
543
|
+
fromJSON(data: { tracks: Track[]; current: Track | null; history: Track[]; loopMode: LoopMode; autoPlay: boolean }): void {
|
|
544
|
+
this.tracks = [...data.tracks];
|
|
545
|
+
this.current = data.current;
|
|
546
|
+
this.history = [...data.history];
|
|
547
|
+
this._loop = data.loopMode;
|
|
548
|
+
this._autoPlay = data.autoPlay;
|
|
549
|
+
}
|
|
342
550
|
/**
|
|
343
551
|
* Get a track at a specific index
|
|
344
552
|
*
|
|
@@ -351,4 +559,41 @@ export class Queue {
|
|
|
351
559
|
getTrack(index: number): Track | null {
|
|
352
560
|
return this.tracks[index] || null;
|
|
353
561
|
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Find tracks by predicate
|
|
565
|
+
*
|
|
566
|
+
* @param {(track: Track) => boolean} predicate - Search function
|
|
567
|
+
* @returns {Track[]} Matching tracks
|
|
568
|
+
* @example
|
|
569
|
+
* const youtubeTracks = queue.findTracks(track => track.source === "youtube");
|
|
570
|
+
*/
|
|
571
|
+
findTracks(predicate: (track: Track) => boolean): Track[] {
|
|
572
|
+
return this.tracks.filter(predicate);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Get the index of a track in the queue
|
|
577
|
+
*
|
|
578
|
+
* @param {string | Track} identifier - Track ID, URL, or Track object
|
|
579
|
+
* @returns {number} Index of the track, -1 if not found
|
|
580
|
+
* @example
|
|
581
|
+
* const index = queue.indexOf(track);
|
|
582
|
+
*/
|
|
583
|
+
indexOf(identifier: string | Track): number {
|
|
584
|
+
if (typeof identifier === "string") {
|
|
585
|
+
return this.tracks.findIndex((t) => t.id === identifier || t.url === identifier);
|
|
586
|
+
}
|
|
587
|
+
return this.tracks.findIndex((t) => t.id === identifier.id);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Check if a track exists in the queue
|
|
592
|
+
*
|
|
593
|
+
* @param {string | Track} identifier - Track ID, URL, or Track object
|
|
594
|
+
* @returns {boolean} True if track exists
|
|
595
|
+
*/
|
|
596
|
+
has(identifier: string | Track): boolean {
|
|
597
|
+
return this.indexOf(identifier) !== -1;
|
|
598
|
+
}
|
|
354
599
|
}
|