From 82655afa20e69b9046a7466b93a0926ecaed6f96 Mon Sep 17 00:00:00 2001 From: saopig1 <4x7sw862st@gmail.com> Date: Wed, 6 May 2026 14:43:01 +0800 Subject: [PATCH] fix(player): retry-skip in /play-playlist when first song can't resolve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQ Music sometimes refuses to issue a play URL for individual songs (upstream returns result=104003, "no copyright/payment"). The /play-playlist endpoint only called resolveAndPlay once on the first song, so when that one was 104003 the bot would sit silently with no feedback — exactly what looked like "播放全部 没有反应". Make BotInstance.playNext() public and call it as a fallback when the first resolveAndPlay fails. playNext already has the 3-attempt retry-skip used by trackEnd auto-advance, so we get the same graceful skip behavior for explicit playlist plays. Also tighten the response message so the user can tell when the bot loaded songs but none were playable. Single-song /play-song still returns "Cannot play" on failure (no queue to advance through); UX surfacing of that goes in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/bot/instance.ts | 8 +++++++- src/web/api/player.ts | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/bot/instance.ts b/src/bot/instance.ts index 8af5c0b..0478483 100755 --- a/src/bot/instance.ts +++ b/src/bot/instance.ts @@ -734,7 +734,13 @@ export class BotInstance extends EventEmitter { ].join("\n"); } - private async playNext(): Promise { + /** + * 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 + * seed the queue can fall back to this retry-skip behavior. + */ + async playNext(): Promise { if (this.isAdvancing || !this.connected) return; this.isAdvancing = true; try { diff --git a/src/web/api/player.ts b/src/web/api/player.ts index 312ee6a..68f7cb6 100644 --- a/src/web/api/player.ts +++ b/src/web/api/player.ts @@ -265,11 +265,22 @@ export function createPlayerRouter( first = queue.play(); } - if (first) { - await bot.resolveAndPlay(first); + // 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. + let started = first ? await bot.resolveAndPlay(first) : false; + if (first && !started) { + await bot.playNext(); + started = !!queue.current(); } - res.json({ message: `Loaded ${songs.length} songs. Now playing: ${first?.name ?? "unknown"}` }); + const playing = queue.current(); + if (started && playing) { + res.json({ message: `Loaded ${songs.length} songs. Now playing: ${playing.name}` }); + } else { + res.json({ message: `Loaded ${songs.length} songs but none were playable (likely copyright/region restrictions).` }); + } } catch (err) { logger.error({ err }, "Play playlist failed"); res.status(500).json({ error: (err as Error).message });