Files
youlbot-webui/services/tts.py
T
shinalok 1e93def909 Refactor: split services.py into services/ package
ChatService → services/chat.py
DocumentService → services/document.py
TTSService → services/tts.py
services/__init__.py re-exports all three for backward-compatible imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 17:39:52 +09:00

54 lines
1.6 KiB
Python

import asyncio
import platform
import subprocess
import tempfile
from config import AppConfig
class TTSService:
def __init__(self, config: AppConfig):
self._voice = config.tts_voice
self._edge_voice = config.tts_edge_voice
async def speak(self, text: str) -> str | None:
"""크로스플랫폼 TTS. macOS: say→edge-tts→pyttsx3 / Windows: edge-tts→pyttsx3"""
if not text:
return None
if platform.system() == "Darwin":
try:
tmp = tempfile.NamedTemporaryFile(suffix=".aiff", delete=False)
tmp.close()
await asyncio.to_thread(
subprocess.run,
["say", "-v", self._voice, "-o", tmp.name, text],
check=True,
capture_output=True,
)
return tmp.name
except Exception:
pass
try:
import edge_tts
tmp = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
tmp.close()
await edge_tts.Communicate(text, self._edge_voice).save(tmp.name)
return tmp.name
except Exception:
pass
try:
import pyttsx3
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
tmp.close()
def _save():
engine = pyttsx3.init()
engine.save_to_file(text, tmp.name)
engine.runAndWait()
await asyncio.to_thread(_save)
return tmp.name
except Exception:
return None