mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
feat(queue): add forwardStack for reversible prev/next navigation
In Random/RandomLoop modes, prev now records the current position to a forwardStack, and next pops from it first. This ensures prev→next navigation is fully reversible for any number of steps (up to 50). Also rebuild playedIndices in prev() so songs reached via backward navigation become available for random selection again.
This commit is contained in:
1 parent
6b60792277
commit
63f1ced2bc
1 file changed
+27
-4
+27
-4
@@ -22,6 +22,7 @@ export class PlayQueue {
|
|||||||
private mode: PlayMode = PlayMode.Sequential;
|
private mode: PlayMode = PlayMode.Sequential;
|
||||||
private playedIndices = new Set<number>();
|
private playedIndices = new Set<number>();
|
||||||
private history: number[] = [];
|
private history: number[] = [];
|
||||||
|
private forwardStack: number[] = [];
|
||||||
private static readonly HISTORY_LIMIT = 50;
|
private static readonly HISTORY_LIMIT = 50;
|
||||||
|
|
||||||
private pushHistory(idx: number): void {
|
private pushHistory(idx: number): void {
|
||||||
@@ -100,12 +101,14 @@ export class PlayQueue {
|
|||||||
this.currentIndex = -1;
|
this.currentIndex = -1;
|
||||||
this.playedIndices.clear();
|
this.playedIndices.clear();
|
||||||
this.history = [];
|
this.history = [];
|
||||||
|
this.forwardStack = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
play(): QueuedSong | null {
|
play(): QueuedSong | null {
|
||||||
if (this.songs.length === 0) return null;
|
if (this.songs.length === 0) return null;
|
||||||
this.playedIndices.clear();
|
this.playedIndices.clear();
|
||||||
this.history = [];
|
this.history = [];
|
||||||
|
this.forwardStack = [];
|
||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
this.playedIndices.add(0);
|
this.playedIndices.add(0);
|
||||||
return this.songs[0];
|
return this.songs[0];
|
||||||
@@ -118,6 +121,7 @@ export class PlayQueue {
|
|||||||
// shuffle from this point. History tracking is independent and
|
// shuffle from this point. History tracking is independent and
|
||||||
// unaffected by this clear.
|
// unaffected by this clear.
|
||||||
this.playedIndices.clear();
|
this.playedIndices.clear();
|
||||||
|
this.forwardStack = [];
|
||||||
this.currentIndex = index;
|
this.currentIndex = index;
|
||||||
this.playedIndices.add(index);
|
this.playedIndices.add(index);
|
||||||
return this.songs[index];
|
return this.songs[index];
|
||||||
@@ -139,7 +143,20 @@ export class PlayQueue {
|
|||||||
this.currentIndex = (this.currentIndex + 1) % this.songs.length;
|
this.currentIndex = (this.currentIndex + 1) % this.songs.length;
|
||||||
return this.songs[this.currentIndex];
|
return this.songs[this.currentIndex];
|
||||||
}
|
}
|
||||||
case PlayMode.Random: {
|
case PlayMode.Random:
|
||||||
|
case PlayMode.RandomLoop: {
|
||||||
|
// 优先回到前进栈记录的位置(prev 退回的歌)
|
||||||
|
if (this.forwardStack.length > 0) {
|
||||||
|
const target = this.forwardStack.pop()!;
|
||||||
|
if (target !== this.currentIndex) {
|
||||||
|
this.pushHistory(this.currentIndex);
|
||||||
|
this.currentIndex = target;
|
||||||
|
this.playedIndices.add(target);
|
||||||
|
return this.songs[target];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 前进栈为空,走纯随机逻辑
|
||||||
|
if (this.mode === PlayMode.Random) {
|
||||||
const unplayed: number[] = [];
|
const unplayed: number[] = [];
|
||||||
for (let i = 0; i < this.songs.length; i++) {
|
for (let i = 0; i < this.songs.length; i++) {
|
||||||
if (!this.playedIndices.has(i)) unplayed.push(i);
|
if (!this.playedIndices.has(i)) unplayed.push(i);
|
||||||
@@ -151,8 +168,7 @@ export class PlayQueue {
|
|||||||
this.currentIndex = nextIndex;
|
this.currentIndex = nextIndex;
|
||||||
this.playedIndices.add(nextIndex);
|
this.playedIndices.add(nextIndex);
|
||||||
return this.songs[nextIndex];
|
return this.songs[nextIndex];
|
||||||
}
|
} else {
|
||||||
case PlayMode.RandomLoop: {
|
|
||||||
if (this.songs.length === 1) {
|
if (this.songs.length === 1) {
|
||||||
this.pushHistory(this.currentIndex);
|
this.pushHistory(this.currentIndex);
|
||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
@@ -168,17 +184,23 @@ export class PlayQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prev(): QueuedSong | null {
|
prev(): QueuedSong | null {
|
||||||
if (this.songs.length === 0) return null;
|
if (this.songs.length === 0) return null;
|
||||||
|
|
||||||
|
// 记录当前位置到前进栈,供 next 优先返回
|
||||||
|
if (this.currentIndex >= 0 && this.forwardStack.length < PlayQueue.HISTORY_LIMIT) {
|
||||||
|
this.forwardStack.push(this.currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
// Preferred: pop from the back-stack so prev means "the song I
|
// Preferred: pop from the back-stack so prev means "the song I
|
||||||
// actually played before this one," not "the previous array slot."
|
// actually played before this one," not "the previous array slot."
|
||||||
while (this.history.length > 0) {
|
while (this.history.length > 0) {
|
||||||
const idx = this.history.pop()!;
|
const idx = this.history.pop()!;
|
||||||
if (idx >= 0 && idx < this.songs.length) {
|
if (idx >= 0 && idx < this.songs.length) {
|
||||||
this.currentIndex = idx;
|
this.currentIndex = idx;
|
||||||
this.playedIndices.add(idx);
|
this.playedIndices = new Set([...this.history, this.currentIndex]);
|
||||||
return this.songs[idx];
|
return this.songs[idx];
|
||||||
}
|
}
|
||||||
// Stale entry (song removed) — keep popping.
|
// Stale entry (song removed) — keep popping.
|
||||||
@@ -227,6 +249,7 @@ export class PlayQueue {
|
|||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.playedIndices.clear();
|
this.playedIndices.clear();
|
||||||
this.history = [];
|
this.history = [];
|
||||||
|
this.forwardStack = [];
|
||||||
if (this.currentIndex >= 0) {
|
if (this.currentIndex >= 0) {
|
||||||
this.playedIndices.add(this.currentIndex);
|
this.playedIndices.add(this.currentIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user