# app.py from app.gradio_app import build_demo from models.tts_router import cleanup_old_audio import os import shutil import stat import huggingface_hub from shutil import which # ----------------------------- # Model: ensure GGUF is present # ----------------------------- def ensure_model() -> str: """ Download the GGUF model into ./models if not already present. Requires in environment: - LLAMACPP_MODEL_PATH (target path including filename) - HF_MODEL_REPO (e.g. Qwen/Qwen2.5-1.5B-Instruct-GGUF) - HF_MODEL_FILE (e.g. qwen2.5-1.5b-instruct-q4_k_m.gguf) """ model_path = os.getenv("LLAMACPP_MODEL_PATH") repo_id = os.getenv("HF_MODEL_REPO") filename = os.getenv("HF_MODEL_FILE") or (os.path.basename(model_path) if model_path else None) if not model_path or not repo_id or not filename: raise RuntimeError("Missing config: set LLAMACPP_MODEL_PATH and HF_MODEL_REPO (optionally HF_MODEL_FILE).") if os.path.exists(model_path): print(f"[MODEL] Found existing model at {model_path}") return model_path os.makedirs(os.path.dirname(model_path), exist_ok=True) print(f"[MODEL] Downloading {filename} from {repo_id} …") local_path = huggingface_hub.hf_hub_download( repo_id=repo_id, filename=filename, local_dir=os.path.dirname(model_path), local_dir_use_symlinks=False, ) # If hf_hub_download wrote elsewhere (cache), move/rename into desired path if os.path.abspath(local_path) != os.path.abspath(model_path): shutil.copyfile(local_path, model_path) print(f"[MODEL] Ready at {model_path}") return model_path def _ensure_exec(fp: str): if fp and os.path.exists(fp): try: os.chmod(fp, 0o755) except Exception as e: print("[WARN] chmod failed for", fp, e) def log_env_for_audio(): print("[ENV] TTS_ENGINE =", os.getenv("TTS_ENGINE", "(unset)")) print("[ENV] PIPER_MODEL=", os.getenv("PIPER_MODEL", "(unset)")) print("[ENV] PIPER_BIN =", os.getenv("PIPER_BIN", "(unset)")) import os, stat def ensure_piper_ready(): bin_path = os.getenv("PIPER_BIN", "piper") model = os.getenv("PIPER_MODEL") espeak = os.getenv("ESPEAK_DATA_PATH") print(f"[PIPER] BIN={bin_path} exists={os.path.exists(bin_path)}") print(f"[PIPER] MODEL={model} exists={os.path.exists(model) if model else None}") print(f"[PIPER] ESPEAK_DATA_PATH={espeak} exists={os.path.exists(espeak) if espeak else None}") # If PIPER_BIN is an absolute/relative file in repo, make it executable. if bin_path and os.path.isfile(bin_path): try: st = os.stat(bin_path) os.chmod(bin_path, st.st_mode | stat.S_IEXEC) print("[PIPER] chmod +x applied to binary") except Exception as e: print("[PIPER] chmod failed:", e) # ------------- # Application # ------------- def main(): # Log a few envs to help debug Spaces log_env_for_audio() # Clean runtime/audio on boot cleanup_old_audio(keep_latest=None) # Ensure model exists locally # ensure_model() # Make sure bundled Piper can run (no downloads on Spaces) ensure_piper_ready() # Launch Gradio demo = build_demo() # On Spaces, share=True is ignored (safe locally) demo.launch(share=True) if __name__ == "__main__": main()