mattrguerra/transcription-pipelinemainPrivate on GitHub, published here in full

diarize.py

Language Python2.9 KB70 lines

transcription_pipeline/diarize.py

  1. """Who spoke when: pyannote speaker diarization through WhisperX.
  2. The default model is WhisperX's own default, pyannote/speaker-diarization-community-1
  3. (the successor to speaker-diarization-3.1); set TP_DIARIZE_MODEL to use another.
  4. It needs a Hugging Face token whose account has accepted that model's terms
  5. (docs/SETUP.md). The model downloads once, then runs locally. Each aligned word
  6. is given the speaker whose turn overlaps it most; shape.py turns that into
  7. sentences.
  8. """
  9. from __future__ import annotations
  10. import inspect
  11. import os
  12. from functools import lru_cache
  13. from pathlib import Path
  14. from .audio import load_wav16k
  15. DEFAULT_MODEL = os.environ.get("TP_DIARIZE_MODEL") or "pyannote/speaker-diarization-community-1"
  16. class DiarizeError(RuntimeError):
  17. pass
  18. def explain_load_error(err: Exception, model: str) -> DiarizeError | None:
  19. """Turn Hugging Face's access errors into what to do about them."""
  20. name = type(err).__name__
  21. text = str(err)
  22. if name == "GatedRepoError" or "gated repo" in text.lower() or "authorized list" in text:
  23. return DiarizeError(
  24. f"Your Hugging Face account has not accepted the terms for {model}. "
  25. f"Open https://huggingface.co/{model} while signed in, accept them, then try again "
  26. "(or run with --no-diarize).")
  27. if "401" in text or "Invalid credentials" in text or "invalid user token" in text.lower():
  28. return DiarizeError("Hugging Face rejected the token. Make a new read token and store it "
  29. "again (docs/SETUP.md, step 2).")
  30. return None
  31. @lru_cache(maxsize=1)
  32. def _pipeline(model: str, token: str, device: str):
  33. from whisperx.diarize import DiarizationPipeline
  34. params = inspect.signature(DiarizationPipeline.__init__).parameters
  35. # Older WhisperX called this argument use_auth_token.
  36. key = "token" if "token" in params else "use_auth_token"
  37. try:
  38. return DiarizationPipeline(model_name=model, device=device, **{key: token})
  39. except Exception as err:
  40. explained = explain_load_error(err, model)
  41. if explained is None:
  42. raise
  43. raise explained from err
  44. def diarize(result: dict, wav: Path, token: str | None, min_speakers: int | None = None,
  45. max_speakers: int | None = None, device: str = "cpu",
  46. model: str = DEFAULT_MODEL) -> dict:
  47. if not token:
  48. raise DiarizeError("No Hugging Face token: set HF_TOKEN or add it to the Keychain, or run "
  49. "with --no-diarize (see docs/SETUP.md).")
  50. import whisperx
  51. audio = load_wav16k(wav)
  52. if min_speakers and min_speakers == max_speakers:
  53. speakers = {"num_speakers": min_speakers}
  54. else:
  55. speakers = {"min_speakers": min_speakers, "max_speakers": max_speakers}
  56. turns = _pipeline(model, token, device)(audio, **speakers)
  57. return {**whisperx.assign_word_speakers(turns, result), "diarized": True}