Ctrl K
ComunidadGuías…

scripts/fetch_voice_model.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Fetch the two voice models into models/.

Not committed, for the same reason FFmpeg is not (see fetch_ffmpeg.py): a
binary in git is paid for by everyone who clones, forever. The installer
build fetches them and bundles them, so an installed copy needs no network.

    python scripts/fetch_voice_model.py

analysis/voice_turns.py uses the pair to tell a second voice from the main
one, so its captions can take another colour:

  - pyannote's segmentation-3.0 (MIT), as exported to ONNX for sherpa-onnx:
    who is talking when, ten seconds at a time.
  - WeSpeaker's VoxCeleb ResNet34-LM (CC BY 4.0): whose voice a turn is.

No account is needed to download either.
"""

import hashlib
import sys
import urllib.request
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
MODELS = ROOT / "models"

# (file, what it is, URL, SHA-256 as Hugging Face publishes it). Each URL is
# pinned to a commit, so the file behind the checksum can't change under it.
WANTED = [
    (
        "wespeaker_resnet34_lm.onnx",
        "WeSpeaker speaker model",
        "https://huggingface.co/Wespeaker/wespeaker-voxceleb-resnet34-LM/resolve/"
        "f0c48c298fd835726c27956a5d617bad7115627e/voxceleb_resnet34_LM.onnx",
        "7bb2f06e9df17cdf1ef14ee8a15ab08ed28e8d0ef5054ee135741560df2ec068",
    ),
    (
        "pyannote_segmentation_3.onnx",
        "pyannote segmentation model",
        "https://huggingface.co/csukuangfj/sherpa-onnx-pyannote-segmentation-3-0/resolve/"
        "9403a6902bb58e3d5ae8c7e77c3422de279db2e0/model.onnx",
        "220ad67ca923bef2fa91f2390c786097bf305bceb5e261d4af67b38e938e1079",
    ),
]


def _sha256(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def _fetch(name: str, label: str, url: str, sha256: str) -> bool:
    dest = MODELS / name
    if dest.exists() and _sha256(dest.read_bytes()) == sha256:
        print(f"Already have {dest.name} ({dest.stat().st_size / 1e6:.1f} MB)")
        return True

    print(f"Fetching the {label} from Hugging Face ...")
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "clips-studio"})
        with urllib.request.urlopen(req, timeout=600) as r:
            data = r.read()
    except Exception as e:
        print(f"  failed: {e.__class__.__name__}: {str(e)[:80]}")
        return False
    if _sha256(data) != sha256:
        print("  the download doesn't match the published checksum, not keeping it")
        return False
    dest.write_bytes(data)
    print(f"  wrote {dest} ({len(data) / 1e6:.1f} MB)")
    return True


def main() -> int:
    MODELS.mkdir(parents=True, exist_ok=True)
    # Not all(): one failing must not stop the other being fetched.
    got = [_fetch(*wanted) for wanted in WANTED]
    if all(got):
        return 0
    print(
        "\nCould not fetch the voice models.\n"
        "Clipping still works without them: captions stay one colour, whoever\n"
        "is talking."
    )
    return 1


if __name__ == "__main__":
    sys.exit(main())