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.
Changes
@@@ -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;@@@ -63,6 +63,8 @@ t.test('the parser reads every filter\'s lines; loud means well above the typicaconst 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 () => {
@@ -130,8 +130,9 @@ function highlightsOf({ duration, signals, perSecond, segments }) {
@@ -130,8 +130,9 @@ function highlightsOf({ duration, signals, perSecond, segments }) {
}
}
const cuts = signals.scene_changes.filter((c) => c.t >= s && c.t < e).length;
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'); }
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'); }
if (talking) { score += 1; reasons.push('speech'); }
const still = dead.reduce((n, d) => n + overlap(s, e, d.start, d.end), 0);
const still = dead.reduce((n, d) => n + overlap(s, e, d.start, d.end), 0);
if ((still / len > 0.5 && !talking) || !score) continue;
if ((still / len > 0.5 && !talking) || !score) continue;
@@ -63,6 +63,8 @@ t.test('the parser reads every filter\'s lines; loud means well above the typica
@@ -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 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 });
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));
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 () => {
t.test('whisper reports the language it detected, not "auto"', async () => {