fix: TTS朗读失败且刷新内容 — 安装edge-tts,前端改用blob播放代替downloadFile

This commit is contained in:
TradeMate Dev
2026-05-17 14:10:45 +08:00
parent f33d33f980
commit 7b7f90d57a
3 changed files with 56 additions and 25 deletions
+1 -3
View File
@@ -111,9 +111,7 @@ async def text_to_speech_get(
if not audio: if not audio:
raise HTTPException(status_code=501, detail="TTS not available") raise HTTPException(status_code=501, detail="TTS not available")
return Response(content=audio, media_type="audio/mpeg", headers={ return Response(content=audio, media_type="audio/mpeg")
"Content-Disposition": f'attachment; filename="tts-{lang}.mp3"',
})
@router.post("/feedback") @router.post("/feedback")
+5 -2
View File
@@ -28,7 +28,7 @@ SUPPORTED_LANGS = list(VOICE_MAP.keys())
class TextToSpeechService: class TextToSpeechService:
@staticmethod @staticmethod
async def synthesize(text: str, lang: str = "en", rate: str = "0%", pitch: str = "0Hz") -> Optional[bytes]: async def synthesize(text: str, lang: str = "en", rate: str = "", pitch: str = "") -> Optional[bytes]:
if not HAS_EDGE_TTS: if not HAS_EDGE_TTS:
logger.warning("edge-tts not available") logger.warning("edge-tts not available")
return None return None
@@ -36,7 +36,10 @@ class TextToSpeechService:
voice = VOICE_MAP.get(lang, VOICE_MAP["en"]) voice = VOICE_MAP.get(lang, VOICE_MAP["en"])
try: try:
communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch) kwargs = {"voice": voice, "rate": rate} if rate else {"voice": voice}
if pitch:
kwargs["pitch"] = pitch
communicate = edge_tts.Communicate(text, **kwargs)
audio_data = b"" audio_data = b""
async for chunk in communicate.stream(): async for chunk in communicate.stream():
if chunk["type"] == "audio": if chunk["type"] == "audio":
+50 -20
View File
@@ -187,29 +187,59 @@ const copyResult = () => {
const playTts = () => { const playTts = () => {
if (!result.value) return if (!result.value) return
const lang = targetLangs[targetIndex.value].code const lang = targetLangs[targetIndex.value].code
const token = uni.getStorageSync('token') const text = result.value
const url = `${BASE_URL}/translate/tts?text=${encodeURIComponent(result.value)}&lang=${lang}`
uni.showLoading({ title: '语音生成中...' }) uni.showLoading({ title: '语音生成中...' })
uni.downloadFile({
url, if (typeof window !== 'undefined' && window.Audio) {
header: { Authorization: `Bearer ${token}` }, const token = uni.getStorageSync('token')
success: (res) => { uni.request({
uni.hideLoading() url: `${BASE_URL}/translate/tts?text=${encodeURIComponent(text)}&lang=${lang}`,
if (res.statusCode === 200) { method: 'GET',
const audioCtx = uni.createInnerAudioContext() header: { Authorization: `Bearer ${token}` },
audioCtx.src = res.tempFilePath responseType: 'arraybuffer',
audioCtx.play() success: (res) => {
audioCtx.onEnded(() => audioCtx.destroy()) uni.hideLoading()
} else { if (res.statusCode === 200 && res.data) {
const blob = new Blob([res.data], { type: 'audio/mpeg' })
const url = URL.createObjectURL(blob)
const audio = new Audio(url)
audio.onended = () => { URL.revokeObjectURL(url) }
audio.play().catch(() => {
uni.showToast({ title: '播放失败,请检查音量', icon: 'none' })
})
} else {
uni.showToast({ title: '语音生成失败', icon: 'none' })
}
},
fail: () => {
uni.hideLoading()
uni.showToast({ title: '语音生成失败', icon: 'none' }) uni.showToast({ title: '语音生成失败', icon: 'none' })
} },
}, })
fail: () => { } else {
uni.hideLoading() const token = uni.getStorageSync('token')
uni.showToast({ title: '语音生成失败', icon: 'none' }) const url = `${BASE_URL}/translate/tts?text=${encodeURIComponent(text)}&lang=${lang}`
}, uni.downloadFile({
}) url,
header: { Authorization: `Bearer ${token}` },
success: (res) => {
uni.hideLoading()
if (res.statusCode === 200) {
const audioCtx = uni.createInnerAudioContext()
audioCtx.src = res.tempFilePath
audioCtx.play()
audioCtx.onEnded(() => audioCtx.destroy())
} else {
uni.showToast({ title: '语音生成失败', icon: 'none' })
}
},
fail: () => {
uni.hideLoading()
uni.showToast({ title: '语音生成失败', icon: 'none' })
},
})
}
} }
const handleExtract = async () => { const handleExtract = async () => {