fix: server-side elapsed tracking via frame count, periodic sync, playlist respects play mode

Progress/lyrics sync:
- AudioPlayer tracks framesPlayed (ground truth: frames * 20ms + seekOffset)
- BotStatus.elapsed is the real elapsed from server
- Frontend polls GET /elapsed every 3s for ground truth
- Client interpolates between syncs (serverElapsed + timeSinceSync)
- Pause freezes at interpolated value, resume resyncs from server
- No more client-side timestamp drift

Playlist play all:
- Stops current playback before loading
- Respects play mode: random/rloop picks random first song, seq plays first

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.6 committed 2026-03-30 03:27:16 +08:00
1 parent 1cdface33c
commit 0439bd047f
6 files changed
+131 -109

No files matched your search

+20 -3
View File
@@ -104,6 +104,12 @@ export function createPlayerRouter(
}
});
// Get current elapsed time (ground truth from server)
router.get("/:botId/elapsed", (req, res) => {
const bot = (req as any).bot;
res.json({ elapsed: bot.getPlayer().getElapsed() });
});
// Seek to position
router.post("/:botId/seek", async (req, res) => {
try {
@@ -153,6 +159,7 @@ export function createPlayerRouter(
});
// Play a playlist by ID — stores metadata only, resolves URL for first song
// Respects current play mode (random = pick random first song)
router.post("/:botId/play-playlist", async (req, res) => {
try {
const bot = (req as any).bot;
@@ -163,21 +170,31 @@ export function createPlayerRouter(
return;
}
// Stop current playback
bot.getPlayer().stop();
const songs = await provider.getPlaylistSongs(playlistId);
if (songs.length === 0) {
res.json({ message: "Playlist is empty" });
return;
}
// Add all songs as metadata only (no URL fetching!)
const queue = bot.getQueueManager();
queue.clear();
for (const song of songs) {
queue.add({ ...song, platform: provider.platform });
}
// Only resolve URL for the first song
const first = queue.play();
// Use queue.play() for sequential, or pick random index for random modes
const mode = queue.getMode();
let first;
if (mode === "random" || mode === "rloop") {
const idx = Math.floor(Math.random() * queue.size());
first = queue.playAt(idx);
} else {
first = queue.play();
}
if (first) {
await bot.resolveAndPlay(first);
}