From 4301da2e375670d414c9cd434d5968743f73daf9 Mon Sep 17 00:00:00 2001 From: saopig1 <4x7sw862st@gmail.com> Date: Wed, 6 May 2026 15:25:59 +0800 Subject: [PATCH] fix(player): bigger retry budget + return value for /play-playlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix for "播放全部 没有反应" hooked into playNext but had two problems: 1. Used !!queue.current() to detect success, which is always true after queue.next() advanced — so the response message would lie about playback starting even when all retries failed. 2. The default 3-retry budget is sized for "next song couldn't resolve, skip it" cases. User-initiated playlist plays commonly hit long contiguous runs of QQ 104003 songs in collected playlists (entire ACG/古风 packs can be uniformly unstreamable in some regions), so 3-4 attempts wasn't enough. Make playNext return whether a song actually started, and accept a maxRetries parameter. /play-playlist now uses 20 retries — high enough to clear typical unplayable runs, bounded enough that fully unstreamable playlists still surface a clear "none were playable" message rather than hanging. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/bot/instance.ts | 22 +++++++++++++--------- src/web/api/player.ts | 10 +++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/bot/instance.ts b/src/bot/instance.ts index 0478483..0174207 100755 --- a/src/bot/instance.ts +++ b/src/bot/instance.ts @@ -736,21 +736,23 @@ export class BotInstance extends EventEmitter { /** * Advance the queue and play the next song. If the resolved URL fails - * (e.g., copyright/region restrictions for QQ), skips up to 3 more - * songs looking for a playable one. Public so REST endpoints that + * (e.g., copyright/region restrictions for QQ), skips up to `maxRetries` + * more songs looking for a playable one. Public so REST endpoints that * seed the queue can fall back to this retry-skip behavior. + * + * Returns true if a song actually started playing, false otherwise. */ - async playNext(): Promise { - if (this.isAdvancing || !this.connected) return; + async playNext(maxRetries = 3): Promise { + if (this.isAdvancing || !this.connected) return false; this.isAdvancing = true; try { this.voteSkipUsers.clear(); const next = this.queue.next(); + let started = false; if (next) { - let started = await this.resolveAndPlay(next); + started = await this.resolveAndPlay(next); if (!started) { - // Skip to next if URL resolve fails (up to 3 retries) - for (let i = 0; i < 3 && this.connected; i++) { + for (let i = 0; i < maxRetries && this.connected; i++) { const retry = this.queue.next(); if (!retry) break; if (await this.resolveAndPlay(retry)) { @@ -772,8 +774,9 @@ export class BotInstance extends EventEmitter { await this.refillFm(); const refillNext = this.queue.next(); if (refillNext) { - await this.resolveAndPlay(refillNext); - } else { + started = await this.resolveAndPlay(refillNext); + } + if (!started) { this.player.stop(); this.profileManager.onSongChange(null).catch(() => {}); } @@ -783,6 +786,7 @@ export class BotInstance extends EventEmitter { } } this.emit("stateChange"); + return started; } finally { this.isAdvancing = false; } diff --git a/src/web/api/player.ts b/src/web/api/player.ts index 68f7cb6..faf74f9 100644 --- a/src/web/api/player.ts +++ b/src/web/api/player.ts @@ -266,13 +266,13 @@ export function createPlayerRouter( } // If the first picked song can't resolve (e.g., QQ song with no - // streaming entitlement → result 104003), fall back to the same - // retry-skip behavior playNext uses for trackEnd auto-advance. - // Otherwise the bot would sit silently on a dead song. + // streaming entitlement → result 104003), fall back to playNext's + // retry-skip behavior. Use a higher retry budget than the default + // trackEnd auto-advance because user-initiated playlist plays + // commonly have long contiguous runs of unplayable songs. let started = first ? await bot.resolveAndPlay(first) : false; if (first && !started) { - await bot.playNext(); - started = !!queue.current(); + started = await bot.playNext(20); } const playing = queue.current();