Files

135 lines
4.3 KiB
Python

"""
TTS Server for ComfyUI Text-to-Speech Node
GeekatPlay Studio
GitHub: https://github.com/GeekatPlayStudio
YouTube: @geekatplay | @geekatplay-ru (Russian)
Patreon: https://patreon.com/geekatplay
Flask server providing text-to-speech via Microsoft Edge TTS with pyttsx3 fallback.
Runs on localhost:5002
Endpoints:
- POST /tts: Generate speech audio from text
- GET /status: Server health check
"""
from flask import Flask, request, jsonify
import os
import uuid
import asyncio
import pyttsx3
import time
import threading
app = Flask(__name__)
OUTPUT_DIR = "outputs"
os.makedirs(OUTPUT_DIR, exist_ok=True)
def cleanup_old_files():
"""Background task to delete temporary audio files older than 1 hour."""
while True:
try:
now = time.time()
cutoff = 3600 # 1 hour
count = 0
for f in os.listdir(OUTPUT_DIR):
f_path = os.path.join(OUTPUT_DIR, f)
if os.path.isfile(f_path):
if now - os.path.getmtime(f_path) > cutoff:
try:
os.remove(f_path)
count += 1
except OSError:
pass
if count > 0:
print(f"[GeekatPlay Cleanup] Removed {count} old temporary files.")
except Exception as e:
print(f"[GeekatPlay Cleanup] Error: {e}")
time.sleep(1800) # Check every 30 minutes
@app.route("/tts", methods=["POST"])
def tts_route():
"""Generate speech audio from text using Edge TTS or pyttsx3 fallback."""
data = request.get_json()
text = data.get("text", "").strip()
voice = data.get("voice", "en-US-AriaNeural").strip()
rate = data.get("rate", 180)
volume = data.get("volume", 1.0)
if not text:
return jsonify({"error": "No text provided"}), 400
# Generate unique filename
wav_name = f"{uuid.uuid4().hex}.wav"
wav_path = os.path.abspath(os.path.join(OUTPUT_DIR, wav_name))
# Try Edge TTS first (high-quality neural voices)
try:
from edge_tts import Communicate
# Convert rate/volume to Edge TTS format
# Rate: +0% format. Base 180.
rate_diff = int(((rate - 180) / 180) * 100)
rate_str = f"{rate_diff:+d}%"
# Volume: +0% format. Base 1.0.
vol_diff = int((volume - 1.0) * 100)
vol_str = f"{vol_diff:+d}%"
communicate = Communicate(text, voice, rate=rate_str, volume=vol_str)
asyncio.run(communicate.save(wav_path))
if os.path.exists(wav_path) and os.path.getsize(wav_path) > 0:
return jsonify({
"ok": True,
"file": wav_name,
"path": wav_path
})
else:
return jsonify({"error": "Failed to generate audio"}), 500
except Exception as e:
# Fallback to pyttsx3 if Edge TTS fails
try:
engine = pyttsx3.init()
engine.setProperty('rate', rate)
engine.setProperty('volume', volume)
# Save speech to file
engine.save_to_file(text, wav_path)
engine.runAndWait()
# Return success response
if os.path.exists(wav_path) and os.path.getsize(wav_path) > 0:
return jsonify({
"ok": True,
"file": wav_name,
"path": wav_path
})
else:
return jsonify({"error": "Failed to generate audio"}), 500
except Exception as e2:
return jsonify({"error": f"TTS failed: {str(e2)}"}), 500
@app.route("/status", methods=["GET"])
def status():
"""Health check endpoint to verify server is running."""
return jsonify({"status": "running", "provider": "GeekatPlay Studio"})
if __name__ == "__main__":
print("\n" + "="*50)
print(" GeekatPlay Studio TTS Server")
print(" Powered by Edge TTS & pyttsx3")
print("="*50 + "\n")
# Start cleanup thread
cleanup_thread = threading.Thread(target=cleanup_old_files, daemon=True)
cleanup_thread.start()
print("[GeekatPlay] Cleanup service started (1 hour retention)")
print("[GeekatPlay] Server listening on http://127.0.0.1:5002")
app.run(host="127.0.0.1", port=5002)