1e93def909
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>
54 lines
1.6 KiB
Python
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
|