UNPKG

@p0llen/speaker-detector-client

Version:

React client for interfacing with the [speaker-detector](https://pypi.org/project/speaker-detector) Python backend. Supports real-time speaker identification, meeting transcription, and speaker enrollment.

139 lines (138 loc) 5.15 kB
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; // speaker-detector-client/src/hooks/useSpeakerDetection.js import { useEffect, useRef, useState } from "react"; import { API_BASE } from "../lib/constants"; import useListeningMode from "./useListeningMode"; export function useSpeakerDetection() { var { endpoint = "/api/active-speaker", log = true } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var { threshold, intervalMs } = useListeningMode("off"); // ← SSOT values var minConfidence = threshold; var interval = intervalMs; var [speaker, setSpeaker] = useState(null); var [confidence, setConfidence] = useState(null); var [isSpeaking, setIsSpeaking] = useState(false); var [status, setStatus] = useState("pending"); var [error, setError] = useState(null); var [backendOnline, setBackendOnline] = useState(null); var lastAnnouncedNameRef = useRef(null); useEffect(() => { var id; var check = /*#__PURE__*/function () { var _ref = _asyncToGenerator(function* () { try { var r = yield fetch("".concat(API_BASE, "/api/version"), { cache: "no-store" }); if (!r.ok) throw new Error(); yield r.json(); setBackendOnline(true); } catch (_unused) { setBackendOnline(false); } }); return function check() { return _ref.apply(this, arguments); }; }(); check(); id = setInterval(check, 5000); return () => clearInterval(id); }, []); useEffect(() => { var id, cancelled = false; var poll = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator(function* () { try { var _d$speaker; var r = yield fetch("".concat(API_BASE).concat(endpoint), { cache: "no-store" }); if (!r.ok) throw new Error("Active speaker API failed"); var d = yield r.json(); if (cancelled) return; var rawSpeaker = (_d$speaker = d.speaker) !== null && _d$speaker !== void 0 ? _d$speaker : null; var rawConfidence = typeof d.confidence === "number" ? d.confidence : null; var backendIsSpeaking = typeof d.is_speaking === "boolean" ? d.is_speaking : null; var stateStatus = (d.status || "pending").trim(); setStatus(stateStatus); setSpeaker(rawSpeaker); setConfidence(rawConfidence); var knownEnough = rawSpeaker && rawSpeaker !== "unknown" && (rawConfidence !== null && rawConfidence !== void 0 ? rawConfidence : 0) >= (minConfidence !== null && minConfidence !== void 0 ? minConfidence : 0.5); var computedIsSpeaking = backendIsSpeaking !== null ? backendIsSpeaking : knownEnough; setIsSpeaking(computedIsSpeaking); // events window.dispatchEvent(new CustomEvent("speaker:update", { detail: { name: rawSpeaker, confidence: rawConfidence, isSpeaking: computedIsSpeaking, status: stateStatus, backendOnline: backendOnline !== false, ts: Date.now() } })); if (knownEnough) { if (lastAnnouncedNameRef.current !== rawSpeaker) { window.dispatchEvent(new CustomEvent("speaker:identified", { detail: { name: rawSpeaker, confidence: rawConfidence, status: stateStatus, ts: Date.now() } })); lastAnnouncedNameRef.current = rawSpeaker; } } else if (lastAnnouncedNameRef.current) { window.dispatchEvent(new CustomEvent("speaker:cleared", { detail: { previous: lastAnnouncedNameRef.current, to: rawSpeaker || "unknown", status: stateStatus, confidence: rawConfidence !== null && rawConfidence !== void 0 ? rawConfidence : 0, ts: Date.now() } })); lastAnnouncedNameRef.current = null; } if (log) { var pct = Math.round((rawConfidence || 0) * 100); console.log("\uD83C\uDF99\uFE0F Detected: ".concat(rawSpeaker, " (").concat(pct, "%) \u2014 ").concat(stateStatus, " | isSpeaking=").concat(computedIsSpeaking)); } setError(null); } catch (e) { if (backendOnline === false) setError("Backend unreachable"); setStatus("error"); setSpeaker(null); setConfidence(null); setIsSpeaking(false); } }); return function poll() { return _ref2.apply(this, arguments); }; }(); poll(); id = setInterval(poll, Math.max(300, interval || 3000)); return () => { cancelled = true; clearInterval(id); }; }, [endpoint, backendOnline, log, interval, minConfidence]); return { speaker, confidence, isSpeaking, error, status, backendOnline }; } export default useSpeakerDetection;