diff --git a/backend/src/modules/tts/tts.controller.ts b/backend/src/modules/tts/tts.controller.ts index 2a5ea62..2e796ae 100644 --- a/backend/src/modules/tts/tts.controller.ts +++ b/backend/src/modules/tts/tts.controller.ts @@ -69,29 +69,38 @@ export class TtsController { } try { + let text = '' if (process.env.OPENAI_API_KEY) { - const result = execSync( - `curl -s -X POST https://api.openai.com/v1/audio/transcriptions \ - -H "Authorization: Bearer ${process.env.OPENAI_API_KEY}" \ - -H "Content-Type: multipart/form-data" \ - -F "file=@${wavPath}" \ - -F "model=whisper-1" \ - -F "language=zh"`, - { encoding: 'utf8', timeout: 30000 }, + try { + const result = execSync( + `curl -s -X POST https://api.openai.com/v1/audio/transcriptions \ + -H "Authorization: Bearer ${process.env.OPENAI_API_KEY}" \ + -H "Content-Type: multipart/form-data" \ + -F "file=@${wavPath}" \ + -F "model=whisper-1" \ + -F "language=zh"`, + { encoding: 'utf8', timeout: 30000 }, + ) + const parsed = JSON.parse(result) + if (parsed.text) text = parsed.text.trim() + } catch (e: any) { + this.logger.warn(`OpenAI ASR failed, falling back to local: ${e.message}`) + } + } + if (!text) { + const whisperResult = execSync( + `python3 -c 'import sys, whisper; model = whisper.load_model("tiny"); print(model.transcribe(sys.argv[1], language="zh")["text"].strip())' "${wavPath}"`, + { encoding: 'utf8', timeout: 60000 }, ) - const parsed = JSON.parse(result) - if (parsed.text) return { text: parsed.text.trim() } - } - const whisperResult = execSync(`python3 -c 'import sys, whisper; model = whisper.load_model("tiny"); print(model.transcribe(sys.argv[1], language="zh")["text"].strip())' "${wavPath}"`, { encoding: 'utf8', timeout: 60000 }) - if (whisperResult && whisperResult.trim()) { - return { text: whisperResult.trim() } + if (whisperResult?.trim()) text = whisperResult.trim() } + return { text } } catch (e: any) { this.logger.error(`ASR failed: ${e?.message || e}`) + return { text: '' } + } finally { + try { if (dest) fs.unlinkSync(dest) } catch {} + try { if (wavPath && wavPath !== dest) fs.unlinkSync(wavPath) } catch {} } - // 清理临时文件 - try { if (dest) fs.unlinkSync(dest) } catch {} - try { if (wavPath && wavPath !== dest) fs.unlinkSync(wavPath) } catch {} - return { text: '' } } } diff --git a/zhiyin-app/src/pages/interview/interview.vue b/zhiyin-app/src/pages/interview/interview.vue index 7c1dfb0..5bd9946 100644 --- a/zhiyin-app/src/pages/interview/interview.vue +++ b/zhiyin-app/src/pages/interview/interview.vue @@ -65,7 +65,7 @@ {{ isRecording ? recordingDuration + 's' : '按住' }} -