Fix: Voice-Auto-Naming tot (TitaNet bricht bei langen Schnitten ab + vergiftete Promise-Kette)

identifySpeakers() embettete den laengsten Lauf ungekuerzt; TitaNet-ONNX bricht
ab 180 s ab (broadcast 12288 by 17998) und riss den spk_embed-Helfer mit. Da
spk.js die Aufrufe ueber eine Promise-Kette serialisiert, schlugen danach ALLE
weiteren Sprecher mit 'spk beendet' fehl.

- ID_CUT_SEC=25s in identifySpeakers
- Kette in spk.js und tdChain in recorder.js failure-isoliert
- pro Name gewinnt nur der beste Kosinus (Fabio war auf 2 Clustern)
This commit is contained in:
Claude
2026-07-27 17:30:43 +02:00
parent 48fa6b4f9e
commit 61531fc50a
5 changed files with 56 additions and 9 deletions
+18 -3
View File
@@ -65,18 +65,33 @@ async function identifySpeakers(recData) {
const len = cur.end - cur.start;
if (!best[cur.speaker] || len > best[cur.speaker].len) best[cur.speaker] = { start: cur.start, end: cur.end, len };
}
const map = {};
// Cut at most ID_CUT_SEC: onnxruntime ABORTS the embedding helper on very long
// inputs (reproduced: 180 s -> "Non-zero status code returned while running
// Where"; 20/60 s fine). On #74 the longest run of a moderator was minutes
// long, which killed the helper on the first speaker and — through the then
// poisoned promise chain in spk.js — every speaker after it ("spk beendet"),
// i.e. voice auto-naming was dead for the whole recording.
const ID_CUT_SEC = 25;
const hits = [];
for (const sp of speakers) {
const r = best[sp]; if (!r) continue;
const seg = `/tmp/idseg_${recData.id}_${sp}.wav`;
const from = r.start / 1000;
const to = Math.min(r.end / 1000, from + ID_CUT_SEC);
try {
await pexec('ffmpeg', ['-y', '-i', wavPath, '-ss', String(r.start / 1000), '-to', String(r.end / 1000), '-ar', '16000', '-ac', '1', seg]);
await pexec('ffmpeg', ['-y', '-i', wavPath, '-ss', String(from), '-to', String(to), '-ar', '16000', '-ac', '1', seg]);
const emb = await spk.embed(seg);
const m = spk.bestMatch(emb, vps);
if (m) map[sp] = m.name;
if (m) hits.push({ speaker: sp, name: m.name, score: m.score });
} catch (e) { console.error('identify', sp, e.message); }
finally { try { fs.unlinkSync(seg); } catch {} }
}
// One enrolled person can only BE one cluster: keep the best-scoring match per
// name (#74 handed "Fabio" to two clusters at once).
const bestByName = {};
for (const h of hits) if (!bestByName[h.name] || h.score > bestByName[h.name].score) bestByName[h.name] = h;
const map = {};
for (const h of Object.values(bestByName)) map[h.speaker] = h.name;
return map;
}