Auto-download Kokoro model files if missing at startup
Browse filesReplaces the FileNotFoundError with _ensure_kokoro_models() which
downloads kokoro-v1.0.onnx and voices-v1.0.bin from the kokoro-onnx
GitHub releases if they are absent — handles the HF Space ephemeral
filesystem without breaking local or modal configurations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- visualnovel/tts.py +22 -10
visualnovel/tts.py
CHANGED
|
@@ -69,6 +69,27 @@ _FEMALE_GENDER_HINTS = {"woman", "female", "her", "she", "girl", "lady"}
|
|
| 69 |
|
| 70 |
_MODEL_PATH = config.MODELS_DIR / "kokoro" / "kokoro-v1.0.onnx"
|
| 71 |
_VOICES_PATH = config.MODELS_DIR / "kokoro" / "voices-v1.0.bin"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
|
| 74 |
def _pick_voice(voice_description: str, sprite_seed: int) -> str:
|
|
@@ -106,16 +127,7 @@ class KokoroTTS:
|
|
| 106 |
def __init__(self) -> None:
|
| 107 |
from kokoro_onnx import Kokoro # noqa: PLC0415
|
| 108 |
|
| 109 |
-
|
| 110 |
-
raise FileNotFoundError(
|
| 111 |
-
f"Kokoro model files missing from {_MODEL_PATH.parent}.\n"
|
| 112 |
-
"Download them with:\n"
|
| 113 |
-
" curl -L -o models/kokoro/kokoro-v1.0.onnx \\\n"
|
| 114 |
-
" https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx\n"
|
| 115 |
-
" curl -L -o models/kokoro/voices-v1.0.bin \\\n"
|
| 116 |
-
" https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin"
|
| 117 |
-
)
|
| 118 |
-
|
| 119 |
self._kokoro = Kokoro(str(_MODEL_PATH), str(_VOICES_PATH))
|
| 120 |
# Filter profiles to voices actually present in the loaded model
|
| 121 |
available = set(self._kokoro.get_voices())
|
|
|
|
| 69 |
|
| 70 |
_MODEL_PATH = config.MODELS_DIR / "kokoro" / "kokoro-v1.0.onnx"
|
| 71 |
_VOICES_PATH = config.MODELS_DIR / "kokoro" / "voices-v1.0.bin"
|
| 72 |
+
_KOKORO_BASE_URL = (
|
| 73 |
+
"https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0"
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def _ensure_kokoro_models() -> None:
|
| 78 |
+
"""Download Kokoro model files if absent (handles HF Space ephemeral filesystem)."""
|
| 79 |
+
files = {
|
| 80 |
+
_MODEL_PATH: f"{_KOKORO_BASE_URL}/kokoro-v1.0.onnx",
|
| 81 |
+
_VOICES_PATH: f"{_KOKORO_BASE_URL}/voices-v1.0.bin",
|
| 82 |
+
}
|
| 83 |
+
missing = {p: u for p, u in files.items() if not p.exists()}
|
| 84 |
+
if not missing:
|
| 85 |
+
return
|
| 86 |
+
import urllib.request
|
| 87 |
+
|
| 88 |
+
_MODEL_PATH.parent.mkdir(parents=True, exist_ok=True)
|
| 89 |
+
for path, url in missing.items():
|
| 90 |
+
print(f"[tts] Downloading {path.name} …", flush=True)
|
| 91 |
+
urllib.request.urlretrieve(url, path)
|
| 92 |
+
print(f"[tts] {path.name} ready ({path.stat().st_size // 1_000_000} MB)", flush=True)
|
| 93 |
|
| 94 |
|
| 95 |
def _pick_voice(voice_description: str, sprite_seed: int) -> str:
|
|
|
|
| 127 |
def __init__(self) -> None:
|
| 128 |
from kokoro_onnx import Kokoro # noqa: PLC0415
|
| 129 |
|
| 130 |
+
_ensure_kokoro_models()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
self._kokoro = Kokoro(str(_MODEL_PATH), str(_VOICES_PATH))
|
| 132 |
# Filter profiles to voices actually present in the loaded model
|
| 133 |
available = set(self._kokoro.get_voices())
|