feat(tts): integrate autonomous Piper neural TTS with zero-latency pre-warming for words and context

This commit is contained in:
2026-10-07 10:40:02 +03:00
parent 915d2784a2
commit a6fb4349e9
12 changed files with 514 additions and 27 deletions
+50
View File
@@ -2230,6 +2230,56 @@ def test_wikipedia_api_endpoints_workflow(monkeypatch):
assert del_resp.json()["deleted_id"] == book_id
def test_piper_tts_endpoints_and_caching():
login_resp = client.post("/api/login", json={"username": "testadmin", "password": "testpass12345"})
assert login_resp.status_code == 200
token = login_resp.cookies.get(config.SESSION_COOKIE_NAME)
auth_headers = {"Cookie": f"{config.SESSION_COOKIE_NAME}={token}"}
# 1. Test TTS Status endpoint
resp = client.get("/api/tts/status", headers=auth_headers)
assert resp.status_code == 200
data = resp.json()
assert "available" in data
assert data["engine"] == "piper-vits"
assert "en_US-lessac" in data["voices"]
# 2. Test TTS synthesis of English word
tts_resp = client.get("/api/tts?text=Astronomy&lang=en&speed=1.0", headers=auth_headers)
assert tts_resp.status_code == 200
assert "audio/wav" in tts_resp.headers.get("content-type", "")
assert len(tts_resp.content) > 1000
# 3. Test repeat request returns cached audio
cached_resp = client.get("/api/tts?text=Astronomy&lang=en&speed=1.0", headers=auth_headers)
assert cached_resp.status_code == 200
assert len(cached_resp.content) == len(tts_resp.content)
# 4. Test cached audio streaming via /api/audio/{hash}.wav
from app.tts import get_audio_hash
h = get_audio_hash("Astronomy", "en", 1.0)
audio_file_resp = client.get(f"/api/audio/{h}.wav")
assert audio_file_resp.status_code == 200
assert "audio/wav" in audio_file_resp.headers.get("content-type", "")
assert "immutable" in audio_file_resp.headers.get("cache-control", "")
# 5. Test card creation attaches audio_url and context_audio_url
card_resp = client.post(
"/api/cards",
json={
"term": "Galileo",
"translation": "Галилей",
"context_sentence": "Galileo pointed his telescope at Jupiter.",
},
headers=auth_headers,
)
assert card_resp.status_code == 200
card_data = card_resp.json()
assert "/api/audio/" in card_data["audio_url"]
assert "context_audio_url" in card_data
assert "/api/audio/" in card_data["context_audio_url"]