fix: resolve critical race conditions and high-severity bugs

- Add isAdvancing guard to playNext() to prevent concurrent calls from
  skipping songs (trackEnd + user !next race condition)
- Replace removeAllListeners in WebSocket setup with tracked named
  listeners, attach only once per bot instead of every 5 seconds
- Add .catch() to unhandled playNext() in cmdVote
- Add sessionId counter to AudioPlayer to discard stale setTimeout
  callbacks after stop()+play() transitions
- Defer nulling TS3 client until disconnect() promise resolves
- Add null checks for providers in play-by-id and add-by-id endpoints
- Add PCM buffer backpressure (pause FFmpeg at 960KB, resume at 480KB)
- Change volume slider from @input to @change to avoid flooding server
- Wrap error-reporting sendTextMessage in try/catch to prevent
  unhandled rejection in error handler
- Clamp elapsed getter to song duration
- Log FFmpeg stderr at debug level instead of swallowing
- Return null from prev() in Sequential mode instead of wrapping

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 14:31:03 +08:00
1 parent 4c71a0a37f
commit 17991b936a
8 files changed
+150 -57

No files matched your search

+10 -2
View File
@@ -211,7 +211,11 @@ export function createPlayerRouter(
try {
const bot = (req as any).bot;
const { songId, platform } = req.body;
const provider = (platform === "qq" ? qqProvider : neteaseProvider)!;
const provider = platform === "qq" ? qqProvider : neteaseProvider;
if (!provider) {
res.status(500).json({ error: "Provider not available" });
return;
}
const song = await provider.getSongDetail(songId);
if (!song) {
@@ -241,7 +245,11 @@ export function createPlayerRouter(
try {
const bot = (req as any).bot;
const { songId, platform } = req.body;
const provider = (platform === "qq" ? qqProvider : neteaseProvider)!;
const provider = platform === "qq" ? qqProvider : neteaseProvider;
if (!provider) {
res.status(500).json({ error: "Provider not available" });
return;
}
const song = await provider.getSongDetail(songId);
if (!song) {