Add /api/mc/tts and /api/mc/voices endpoints, fix login to only show own voices

This commit is contained in:
2026-06-05 20:44:52 -05:00
parent 3ca2bc077f
commit 8dcb228122
+77 -4
View File
@@ -590,7 +590,7 @@ def _resolve_guild_id() -> str:
@app.route("/api/mc/login", methods=["POST"])
def api_mc_login():
"""Minecraft mod login endpoint. Accepts JSON {"token": "..."} and returns user info + voices."""
"""Minecraft mod login endpoint. Accepts JSON {"token": "..."} and returns user info + personal voices."""
data = request.get_json(force=True) if request.is_json else {}
token = data.get("token", "").strip()
if not token:
@@ -603,11 +603,12 @@ def api_mc_login():
user_id = entry["user_id"]
ucfg = get_user_config(user_id)
voices = ucfg.get("chatterbox_voices", [])
# Only return voices that belong to this user (prefixed with their user_id)
result = []
for v in voices:
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})
if v.startswith(f"{user_id}_"):
display = v.split("_", 1)[1].rsplit(".", 1)[0]
result.append({"filename": v, "display": display})
return jsonify({
"user_id": user_id,
@@ -616,6 +617,78 @@ def api_mc_login():
})
@app.route("/api/mc/voices", methods=["GET"])
def api_mc_voices():
"""List all predefined voices from the Chatterbox server."""
try:
chatterbox_url = get_chatterbox_url()
r = requests.get(f"{chatterbox_url}/get_predefined_voices", timeout=10)
r.raise_for_status()
return jsonify(r.json())
except Exception as e:
return jsonify({"error": str(e)}), 502
@app.route("/api/mc/tts", methods=["POST"])
def api_mc_tts():
"""Generate TTS audio. Returns WAV bytes.
JSON body: {"text": "...", "mode": "chatterbox|dectalk|morshu", "voice": "Emily.wav"}
"""
data = request.get_json(force=True) if request.is_json else {}
text = (data.get("text") or "").strip()
if not text:
return jsonify({"error": "Missing text"}), 400
mode = data.get("mode", "chatterbox")
voice = data.get("voice", "Emily.wav")
try:
if mode == "chatterbox":
chatterbox_url = get_chatterbox_url()
payload = {
"text": text,
"voice_mode": "predefined",
"predefined_voice_id": voice,
"output_format": "wav",
"split_text": False,
}
r = requests.post(f"{chatterbox_url}/tts", json=payload, timeout=120)
r.raise_for_status()
return r.content, 200, {"Content-Type": "audio/wav"}
elif mode == "clone":
chatterbox_url = get_chatterbox_url()
payload = {
"text": text,
"voice_mode": "clone",
"reference_audio_filename": voice,
"output_format": "wav",
"split_text": False,
}
r = requests.post(f"{chatterbox_url}/tts", json=payload, timeout=120)
r.raise_for_status()
return r.content, 200, {"Content-Type": "audio/wav"}
elif mode == "dectalk":
dectalk_url = get_dectalk_url()
r = requests.get(f"{dectalk_url}/say", params={"text": text}, timeout=30)
r.raise_for_status()
return r.content, 200, {"Content-Type": "audio/wav"}
elif mode == "morshu":
r = requests.get("http://127.0.0.1:3002/say", params={"text": text}, timeout=60)
r.raise_for_status()
return r.content, 200, {"Content-Type": "audio/wav"}
else:
return jsonify({"error": f"Unknown mode: {mode}"}), 400
except requests.exceptions.ConnectionError:
return jsonify({"error": f"{mode} server not reachable"}), 502
except Exception as e:
return jsonify({"error": str(e)}), 500
# ---------------------------------------------------------------------------
# Routes — Chatterbox voice management
# ---------------------------------------------------------------------------