CPU-only online speaker labelling for caption windows (CAPTION_DIARIZATION=1):
pyannote segmentation-3.0 finds local turns, a TitaNet-small voiceprint via
sherpa-onnx matches them to per-room speakers numbered in arrival order.
/transcribe?speakers=1 returns committed words grouped into segments with a
speaker ID and the name assigned for the show. Models are SHA-256 pinned and
cached with the Whisper weights; audio and voiceprints are never stored.
Rooms gain a persisted cast list and in-memory per-show names through
/api/rooms/<id>/speakers. The capture page offers "Identify speakers
automatically", publishes one relay caption per speaker turn with label and
spk, and shares a speaker-names panel (speaker-names.js) with the editor.
Also adds scripts/diarize_eval.py, deploy.py --speakers, and copies rooms.py
into the GPU image.
The private-relay and cross-origin browser probes still built apps with the
removed CAPTION_API_KEY argument and seeded the relay with hand-written rooms.
They now create a KeyRegistry, derive the probe user's rooms and tokens with the
shared master secret, and configure the relay from that secret. The bundle sync
also rewrites the claim link to the service so the hosted capture page does not
point at a page it cannot serve. A cross-language test pins the derivation
vector shared with the relay's JavaScript implementation.
Replace the shared CAPTION_API_KEY with an optional per-user registry.
keys.py stores 64-character keys and single-use invites as SHA-256 digests
and derives each user's source/output rooms and capability tokens from a
shared master secret. /relay-config now returns those per-key values,
/claim.html redeems invites, and /admin.html (gated by CAPTION_ADMIN_KEY)
mints invites and revokes keys. /health and the static pages are public.