Files

60 lines
1.8 KiB
Python

import hashlib
import os
import subprocess
SERVER_DIR = os.path.dirname(os.path.abspath(__file__))
DEFAULT_CACHE_DIR = os.path.join(SERVER_DIR, "data", "tts_cache")
PIPER_BIN = os.environ.get("PIPER_BIN", "/opt/piper/venv/bin/piper")
PIPER_MODELS_DIR = os.environ.get("PIPER_MODELS_DIR", "/opt/piper/voices")
TTS_CACHE_DIR = os.environ.get("TTS_CACHE_DIR", DEFAULT_CACHE_DIR)
VOICES = {
"es": "es_ES-davefx-medium.onnx",
"en": "en_US-lessac-medium.onnx",
"zh": "zh_CN-huayan-medium.onnx",
}
def piper_available():
return bool(PIPER_BIN) and os.path.isfile(PIPER_BIN)
def generate_speech(text, lang="es"):
voice = VOICES.get(lang, VOICES["en"])
digest = hashlib.sha256((lang + "\0" + text).encode("utf-8")).hexdigest()[:24]
cache_path = os.path.join(TTS_CACHE_DIR, f"{lang}_{digest}.wav")
if os.path.isfile(cache_path):
return cache_path
model_path = os.path.join(PIPER_MODELS_DIR, voice)
if not os.path.isfile(model_path):
raise RuntimeError(
f"Piper 模型不存在: {model_path},请先下载语音模型并配置 PIPER_MODELS_DIR"
)
os.makedirs(TTS_CACHE_DIR, exist_ok=True)
tmp_path = cache_path + ".tmp"
try:
proc = subprocess.run(
[
PIPER_BIN,
"--model",
model_path,
"--output_file",
tmp_path,
],
input=text.encode("utf-8"),
capture_output=True,
timeout=90,
)
except subprocess.TimeoutExpired as exc:
raise RuntimeError("Piper 生成超时") from exc
if proc.returncode != 0 or not os.path.isfile(tmp_path):
stderr = proc.stderr.decode("utf-8", errors="replace")
raise RuntimeError(stderr or "Piper 生成失败")
os.replace(tmp_path, cache_path)
return cache_path