Add standalone morshu-server, update webui to support morshu/dectalk generate
This commit is contained in:
+32
-59
@@ -386,7 +386,7 @@ def tts_job_update(job_id):
|
||||
@app.route("/api/tts/generate", methods=["POST"])
|
||||
@login_required
|
||||
def api_tts_generate():
|
||||
"""Generate TTS via the bot (no VC playback). All modes supported."""
|
||||
"""Generate TTS via the bot or directly via TTS servers."""
|
||||
data = request.get_json(force=True)
|
||||
text = (data.get("text") or "").strip()
|
||||
if not text:
|
||||
@@ -400,8 +400,38 @@ def api_tts_generate():
|
||||
global_mode = gcfg.get("tts_mode", "minimax")
|
||||
mode = data.get("mode") or user_mode_overrides.get(str(user_id), global_mode)
|
||||
|
||||
# Direct server modes — generate audio without the bot
|
||||
if mode == "morshu":
|
||||
try:
|
||||
r = requests.get("http://127.0.0.1:3002/say", params={"text": text}, timeout=60)
|
||||
r.raise_for_status()
|
||||
job_id = str(uuid.uuid4())
|
||||
out_path = tempfile.mktemp(suffix=".wav")
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(r.content)
|
||||
_tts_jobs[job_id] = {"status": "done", "path": out_path, "text": text, "user": discord_name, "engine": "morshu"}
|
||||
return jsonify({"job_id": job_id})
|
||||
except Exception as e:
|
||||
return jsonify({"error": f"Morshu server error: {e}"}), 500
|
||||
|
||||
if mode == "dectalk":
|
||||
try:
|
||||
dectalk_url = gcfg.get("dectalk_api_url", "http://127.0.0.1:3001")
|
||||
r = requests.get(f"{dectalk_url}/say", params={"text": text}, timeout=30)
|
||||
r.raise_for_status()
|
||||
job_id = str(uuid.uuid4())
|
||||
out_path = tempfile.mktemp(suffix=".wav")
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(r.content)
|
||||
_tts_jobs[job_id] = {"status": "done", "path": out_path, "text": text, "user": discord_name, "engine": "dectalk"}
|
||||
return jsonify({"job_id": job_id})
|
||||
except Exception as e:
|
||||
return jsonify({"error": f"DECTalk server error: {e}"}), 500
|
||||
|
||||
if mode != "chatterbox":
|
||||
return jsonify({"error": f"Generate Only is not available for '{mode}' mode via web UI."}), 400
|
||||
|
||||
voice = data.get("voice") or None
|
||||
post_to_discord = data.get("post_to_discord", True)
|
||||
job_id = str(uuid.uuid4())
|
||||
_tts_jobs[job_id] = {"status": "pending", "text": text, "user": discord_name, "engine": mode}
|
||||
|
||||
@@ -412,7 +442,6 @@ def api_tts_generate():
|
||||
"mode_override": mode,
|
||||
"voice_override": voice,
|
||||
"skip_vc": True,
|
||||
"skip_post": not post_to_discord,
|
||||
"guild_id": _resolve_guild_id(),
|
||||
})
|
||||
|
||||
@@ -565,48 +594,11 @@ def api_chatterbox_voices():
|
||||
user_id = session["user_id"]
|
||||
ucfg = get_user_config(user_id)
|
||||
voices = ucfg.get("chatterbox_voices", [])
|
||||
|
||||
# Validate voices exist on the Chatterbox server
|
||||
cb_url = get_chatterbox_url()
|
||||
server_voices = set()
|
||||
try:
|
||||
r = requests.get(f"{cb_url.rstrip('/')}/api/ui/initial-data", timeout=3)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
server_voices = set(data.get("predefined_voices", []))
|
||||
except Exception:
|
||||
pass # If server unreachable, show all voices without filtering
|
||||
|
||||
result = []
|
||||
stale = []
|
||||
for v in voices:
|
||||
if server_voices and v not in server_voices:
|
||||
stale.append(v)
|
||||
continue
|
||||
parts = v.split("_", 1)
|
||||
display = parts[1].rsplit(".", 1)[0] if len(parts) > 1 else v.rsplit(".", 1)[0]
|
||||
result.append({"filename": v, "display": display})
|
||||
|
||||
# Auto-clean stale voices from config
|
||||
if stale:
|
||||
try:
|
||||
p = COG_CONFIG_PATH / "settings.json"
|
||||
data = _read_json(p)
|
||||
if data:
|
||||
ucfg_data = data.get(COG_IDENTIFIER, {}).get("USER", {}).get(str(user_id), {})
|
||||
cfg_voices = ucfg_data.get("chatterbox_voices", [])
|
||||
for s in stale:
|
||||
if s in cfg_voices:
|
||||
cfg_voices.remove(s)
|
||||
ucfg_data["chatterbox_voices"] = cfg_voices
|
||||
if ucfg_data.get("minimax_voice") in stale:
|
||||
ucfg_data["minimax_voice"] = cfg_voices[0] if cfg_voices else None
|
||||
with open(p, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
log.info(f"Auto-cleaned {len(stale)} stale voices for user {user_id}: {stale}")
|
||||
except Exception as ex:
|
||||
log.warning(f"Could not auto-clean stale voices: {ex}")
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@@ -670,25 +662,6 @@ def api_chatterbox_delete_voice(filename):
|
||||
return jsonify({"error": f"Server returned {r.status_code}"}), 500
|
||||
except Exception as ex:
|
||||
return jsonify({"error": str(ex)}), 500
|
||||
|
||||
# Immediately remove from settings.json so the UI updates without waiting for bot
|
||||
try:
|
||||
p = COG_CONFIG_PATH / "settings.json"
|
||||
data = _read_json(p)
|
||||
if data:
|
||||
ucfg = data.get(COG_IDENTIFIER, {}).get("USER", {}).get(str(user_id), {})
|
||||
voices = ucfg.get("chatterbox_voices", [])
|
||||
if filename in voices:
|
||||
voices.remove(filename)
|
||||
ucfg["chatterbox_voices"] = voices
|
||||
# If active voice was deleted, switch to first remaining or clear
|
||||
if ucfg.get("minimax_voice") == filename:
|
||||
ucfg["minimax_voice"] = voices[0] if voices else None
|
||||
with open(p, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
except Exception as ex:
|
||||
log.warning(f"Could not update settings.json directly: {ex}")
|
||||
|
||||
_write_bot_command(user_id, "remove_voice", {"filename": filename})
|
||||
return jsonify({"ok": True})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user