Add offline desktop speech with Silero Xenia for Russian
This commit is contained in:
1 parent
9cd8ebc422
commit
9dbe30b20c
28 files changed
+1865
-16
No files matched your search
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Workspace-local RuAccent CPU setup. No sudo or changes to system Python."""
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import runpy
|
||||
import shutil
|
||||
import subprocess
|
||||
import urllib.request
|
||||
import zipfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def main():
|
||||
uv = shutil.which("uv") or str(ROOT / ".local/tools/uv")
|
||||
if not Path(uv).is_file():
|
||||
if platform.system() != "Linux" or platform.machine() != "x86_64":
|
||||
raise SystemExit("Install uv first on this platform, then rerun this setup script.")
|
||||
with urllib.request.urlopen("https://pypi.org/pypi/uv/0.12.19/json", timeout=60) as source:
|
||||
metadata = json.load(source)
|
||||
wheel = next(x for x in metadata["urls"] if x["filename"].endswith("manylinux_2_17_x86_64.manylinux2014_x86_64.whl"))
|
||||
with urllib.request.urlopen(wheel["url"], timeout=60) as source:
|
||||
content = source.read()
|
||||
expected = "a63d18a0aa38ee9f21a5406afbbaeb41303bcd954be9d6b7c1b95ac275e53958"
|
||||
if hashlib.sha256(content).hexdigest() != expected:
|
||||
raise RuntimeError("uv checksum mismatch")
|
||||
with zipfile.ZipFile(io.BytesIO(content)) as archive:
|
||||
name = next(x for x in archive.namelist() if x.endswith("/uv"))
|
||||
Path(uv).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(uv).write_bytes(archive.read(name))
|
||||
Path(uv).chmod(0o755)
|
||||
environment = dict(os.environ, UV_CACHE_DIR=str(ROOT / ".local/uv-cache"),
|
||||
UV_PYTHON_INSTALL_DIR=str(ROOT / ".local/python"), UV_SYSTEM_CERTS="true")
|
||||
venv = ROOT / ".local/ruaccent-runtime"
|
||||
python = venv / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
|
||||
if not python.is_file():
|
||||
subprocess.run([uv, "venv", "--python", "3.12.14", str(venv)], env=environment, check=True)
|
||||
subprocess.run([uv, "pip", "sync", "--python", str(python), str(ROOT / "scripts/ruaccent-requirements.txt")], env=environment, check=True)
|
||||
model = ROOT / ".local/tts/ruaccent"
|
||||
manifest = json.loads((ROOT / "scripts/ruaccent-model.json").read_text())
|
||||
runpy.run_path(str(ROOT / "scripts/download-tts-model.py"))["download"](manifest, model)
|
||||
# RUAccent 1.5.8.3 expects its rule-engine files beside the installed package.
|
||||
module_path = subprocess.check_output([str(python), "-c", "import importlib.util; print(importlib.util.find_spec('ruaccent').submodule_search_locations[0])"], text=True).strip()
|
||||
shutil.copytree(model / "koziev", Path(module_path) / "koziev", dirs_exist_ok=True)
|
||||
worker = ROOT / "composeApp/src/jvmMain/resources/tts/ruaccent-worker.py"
|
||||
shutil.copyfile(worker, model / "worker.py")
|
||||
# Verify a wholly offline initialization before declaring the installation ready.
|
||||
subprocess.run([str(python), str(model / "worker.py"), str(model), "--check"], check=True, env=environment)
|
||||
(model / "runtime.json").write_text(json.dumps({"python": str(python), "model": "turbo3.1", "revision": manifest["revision"]}, indent=2) + "\n")
|
||||
print("RuAccent ready: CPU, turbo3.1, full dictionary; no network during inference.", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user