modelbeast/server/operators/lipsync_local/run.py
type-two 110b2298b7 audio/video/text operator suite: stt_local, lipsync_local, voice_clone_local, wan_video, llm_local
- stt_local: Whisper large-v3-turbo MLX, transcript + timestamped json
- lipsync_local: Rhubarb visemes (CPU lane), dialog-guided
- voice_clone_local: Chatterbox on MPS (needs setuptools<81 for perth/pkg_resources)
- wan_video: Wan 2.2 TI2V-5B t2v/i2v via resident ComfyUI, frames->mp4
- llm_local: Qwen3-30B-A3B-4bit dialogue writer (primary-only)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:26:51 +10:00

50 lines
1.5 KiB
Python

import argparse
import json
import subprocess
import sys
import time
from pathlib import Path
ap = argparse.ArgumentParser()
ap.add_argument("--input", action="append", default=[])
ap.add_argument("--outdir", required=True)
ap.add_argument("--params", default="{}")
a = ap.parse_args()
p = json.loads(a.params)
if not a.input:
print("ERROR: needs one WAV/OGG input")
sys.exit(1)
root = Path(__file__).resolve().parents[3]
rhubarb = root / "vendor" / "rhubarb" / "rhubarb"
if not rhubarb.exists():
print("ERROR: rhubarb not installed — run scripts/install_rhubarb.sh")
sys.exit(1)
outdir = Path(a.outdir)
out_json = outdir / (Path(a.input[0]).stem + ".visemes.json")
cmd = [str(rhubarb), "-f", "json", "-o", str(out_json), a.input[0]]
if p.get("shapes", "extended") == "basic":
cmd += ["--extendedShapes", ""]
dialog = (p.get("dialog") or "").strip()
if dialog:
tmp = outdir / "work" # subdir: only top-level outdir files register as assets
tmp.mkdir(exist_ok=True)
dialog_file = tmp / "dialog.txt"
dialog_file.write_text(dialog + "\n")
cmd += ["--dialogFile", str(dialog_file)]
t0 = time.time()
r = subprocess.run(cmd, capture_output=True, text=True)
if r.stderr:
print(r.stderr[-1500:], file=sys.stderr)
if r.returncode != 0 or not out_json.exists():
print("ERROR: rhubarb failed")
sys.exit(1)
cues = json.loads(out_json.read_text())["mouthCues"]
print(f"done in {time.time() - t0:.1f}s: {len(cues)} mouth cues, "
f"{cues[-1]['end'] if cues else 0:.2f}s")