openvibers/openvibe.ai Commits

media.analyze: a highlight's speech is measured in time (at least half the window inside transcript segments), not in space-separated words, so Japanese speech counts (production: JapaneseOldGuy's VOD had loud and scene-change highlights but never speech). Test: a Japanese segment gives a speech highlight.

OP OpenVibers committed 27 Sept 2026 · e587ce1d49 · 2 files, +5 −2


Changes

ddiff --git a/server/workflows/media-analysis.js b/server/workflows/media-analysis.js
iindex d59be08..6ee87d3 100644
---- a/server/workflows/media-analysis.js
++++ b/server/workflows/media-analysis.js
@@@ -130,8 +130,9 @@ function highlightsOf({ duration, signals, perSecond, segments }) {
}
const cuts = signals.scene_changes.filter((c) => c.t >= s && c.t < e).length;
if (cuts >= 2) { score += Math.min(2, cuts * 0.5); reasons.push('scene changes'); }
-- const words = segments.reduce((n, g) => n + (overlap(s, e, g.start, g.end) > 0 ? g.text.split(/\s+/).filter(Boolean).length : 0), 0);
-- const talking = words / len >= 1.5;
++ // Speech: at least half the window inside transcript segments (time, not words: Japanese has no spaces).
++ const spoken = union(segments.filter((g) => g.text && g.text.trim())).reduce((n, g) => n + overlap(s, e, g.start, g.end), 0);
++ const talking = spoken / len >= 0.5;
if (talking) { score += 1; reasons.push('speech'); }
const still = dead.reduce((n, d) => n + overlap(s, e, d.start, d.end), 0);
if ((still / len > 0.5 && !talking) || !score) continue;
ddiff --git a/test/media-analysis.test.js b/test/media-analysis.test.js
iindex c8bb5b3..16a5b01 100644
---- a/test/media-analysis.test.js
++++ b/test/media-analysis.test.js
@@@ -63,6 +63,8 @@ t.test('the parser reads every filter\'s lines; loud means well above the typica
const segs = Array.from({ length: 12 }, (_, i) => ({ start: i * 5, end: i * 5 + 5, text: 'and then we go over there and look at this one more time okay' }));
const hl = ma.highlightsOf({ duration: 90, signals: talk, perSecond: new Map(), segments: segs });
assert.ok(hl.length && hl.every((x) => x.reasons.includes('speech') && x.start < 60), JSON.stringify(hl));
++ const ja = ma.highlightsOf({ duration: 60, signals: { ...talk, black: [], silence: [] }, perSecond: new Map(), segments: [{ start: 0, end: 25, text: 'これしか使えないのかな' }] });
++ assert.ok(ja.some((x) => x.reasons.includes('speech')), 'speech is measured in time, so a language without spaces counts');
});
t.test('whisper reports the language it detected, not "auto"', async () => {