From dd00b38304d5ed0184fcdb9f94101a49f742f4a4 Mon Sep 17 00:00:00 2001 From: yuanhe Date: Mon, 11 May 2026 11:47:45 +0800 Subject: [PATCH] fix: decode SSE audio stream properly for music and speech Music streaming was writing raw SSE text instead of decoded audio bytes. Two root causes: - extra_info filter skipped all music events (null !== undefined is true) - AbortSignal.timeout aborted long-running streaming responses Extracted pipeAudioStream() to shared util; uses status===2 to skip the terminal summary event. Disabled fetch timeout for streaming. --- src/client/http.ts | 2 +- src/commands/music/cover.ts | 10 ++-------- src/commands/music/generate.ts | 12 +++--------- src/commands/speech/synthesize.ts | 11 ++--------- src/utils/audio-stream.ts | 29 +++++++++++++++++++++++++++++ 5 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 src/utils/audio-stream.ts diff --git a/src/client/http.ts b/src/client/http.ts index 7e327d6..c77ba79 100644 --- a/src/client/http.ts +++ b/src/client/http.ts @@ -62,7 +62,7 @@ export async function request(config: Config, opts: RequestOpts): Promise { + process.stdout.on('error', (err: NodeJS.ErrnoException) => { + if (err.code === 'EPIPE') process.exit(0); + throw err; + }); + + for await (const event of parseSSE(response)) { + if (!event.data || event.data === '[DONE]') break; + + let parsed: SseAudioPayload; + try { parsed = JSON.parse(event.data); } catch { continue; } + + if (parsed.data?.status === 2) continue; + + const hex = parsed.data?.audio; + if (!hex) continue; + + const chunk = Buffer.from(hex, 'hex'); + if (!process.stdout.write(chunk)) { + await new Promise(r => process.stdout.once('drain', r)); + } + } +}