Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2877ae996d | ||
|
|
dbafd4fb73 | ||
|
|
2208a1a7d4 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,3 +3,4 @@ uvicorn==0.27.1
|
|||||||
python-multipart==0.0.9
|
python-multipart==0.0.9
|
||||||
aiohttp==3.9.3
|
aiohttp==3.9.3
|
||||||
edge-tts==6.1.9
|
edge-tts==6.1.9
|
||||||
|
requests==2.31.0
|
||||||
+14
-7
@@ -372,9 +372,9 @@
|
|||||||
<div class="name">🌐 Edge TTS</div>
|
<div class="name">🌐 Edge TTS</div>
|
||||||
<div class="status" id="edgeStatus">检测中...</div>
|
<div class="status" id="edgeStatus">检测中...</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tts-option disabled" data-provider="chattts">
|
<div class="tts-option" data-provider="chattts">
|
||||||
<div class="name">🤖 ChatTTS</div>
|
<div class="name">🤖 ChatTTS</div>
|
||||||
<div class="status">暂未部署</div>
|
<div class="status" id="chatttsStatus">检测中...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="voice-select" id="voiceSelect" style="display: none;">
|
<div class="voice-select" id="voiceSelect" style="display: none;">
|
||||||
@@ -490,13 +490,20 @@
|
|||||||
|
|
||||||
// 更新状态
|
// 更新状态
|
||||||
data.providers.forEach(p => {
|
data.providers.forEach(p => {
|
||||||
if (p.name === 'edge') {
|
const statusElId = p.name === 'edge' ? 'edgeStatus' :
|
||||||
const statusEl = document.getElementById('edgeStatus');
|
p.name === 'chattts' ? 'chatttsStatus' : null;
|
||||||
|
|
||||||
|
if (statusElId) {
|
||||||
|
const statusEl = document.getElementById(statusElId);
|
||||||
statusEl.textContent = p.available ? '可用 ✓' : '不可用';
|
statusEl.textContent = p.available ? '可用 ✓' : '不可用';
|
||||||
|
|
||||||
const optionEl = ttsOptions.querySelector('[data-provider="edge"]');
|
const optionEl = ttsOptions.querySelector(`[data-provider="${p.name}"]`);
|
||||||
if (p.available) {
|
if (optionEl) {
|
||||||
optionEl.classList.remove('disabled');
|
if (p.available) {
|
||||||
|
optionEl.classList.remove('disabled');
|
||||||
|
} else {
|
||||||
|
optionEl.classList.add('disabled');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+40
-13
@@ -108,32 +108,59 @@ class EdgeTTSProvider(TTSProvider):
|
|||||||
|
|
||||||
|
|
||||||
class ChatTTSProvider(TTSProvider):
|
class ChatTTSProvider(TTSProvider):
|
||||||
"""ChatTTS 提供者(本地部署,预留接口)"""
|
"""ChatTTS 提供者(本地部署)"""
|
||||||
|
|
||||||
# 预留配置
|
# ChatTTS 服务地址
|
||||||
CHATTTS_URL = os.getenv("CHATTTS_URL", "http://localhost:19020")
|
CHATTTS_URL = os.getenv("CHATTTS_URL", "http://192.168.2.5:12002")
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._available = False # 暂不可用
|
self._available = None
|
||||||
|
|
||||||
async def synthesize(self, text: str) -> Tuple[str, str]:
|
async def synthesize(self, text: str) -> Tuple[str, str]:
|
||||||
"""
|
"""使用 ChatTTS 合成语音"""
|
||||||
使用 ChatTTS 合成语音
|
import aiohttp
|
||||||
TODO: 后续实现
|
|
||||||
"""
|
async with aiohttp.ClientSession() as session:
|
||||||
raise NotImplementedError("ChatTTS 尚未实现,请先部署 ChatTTS 服务")
|
form = aiohttp.FormData()
|
||||||
|
form.add_field('text', text)
|
||||||
|
|
||||||
|
async with session.post(
|
||||||
|
f"{self.CHATTTS_URL}/synthesize",
|
||||||
|
data=form,
|
||||||
|
timeout=aiohttp.ClientTimeout(total=60)
|
||||||
|
) as resp:
|
||||||
|
if resp.status != 200:
|
||||||
|
error = await resp.text()
|
||||||
|
raise Exception(f"ChatTTS error: {error}")
|
||||||
|
|
||||||
|
data = await resp.json()
|
||||||
|
# ChatTTS 返回的 URL 是相对路径,需要拼接
|
||||||
|
audio_url = f"{self.CHATTTS_URL}{data['audio_url']}"
|
||||||
|
return None, audio_url
|
||||||
|
|
||||||
def get_name(self) -> str:
|
def get_name(self) -> str:
|
||||||
return "ChatTTS"
|
return "ChatTTS"
|
||||||
|
|
||||||
def is_available(self) -> bool:
|
def is_available(self) -> bool:
|
||||||
"""检查 ChatTTS 是否可用"""
|
"""检查 ChatTTS 是否可用"""
|
||||||
# TODO: 后续实现检测逻辑
|
if self._available is None:
|
||||||
|
try:
|
||||||
|
import requests
|
||||||
|
resp = requests.get(f"{self.CHATTTS_URL}/health", timeout=5)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
data = resp.json()
|
||||||
|
self._available = data.get("status") == "ok"
|
||||||
|
else:
|
||||||
|
self._available = False
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"ChatTTS check failed: {e}")
|
||||||
|
self._available = False
|
||||||
return self._available
|
return self._available
|
||||||
|
|
||||||
def set_available(self, available: bool):
|
def set_url(self, url: str):
|
||||||
"""设置可用状态(部署后调用)"""
|
"""设置服务地址"""
|
||||||
self._available = available
|
self.CHATTTS_URL = url
|
||||||
|
self._available = None # 重新检测
|
||||||
|
|
||||||
|
|
||||||
class NoTTSProvider(TTSProvider):
|
class NoTTSProvider(TTSProvider):
|
||||||
|
|||||||
Reference in New Issue
Block a user