fix: TTS朗读失败且刷新内容 — 安装edge-tts,前端改用blob播放代替downloadFile
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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 () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user