openvibers/openvibe.live Commits
AI moment and clip titles also pass the shared looks rule (db.hasLooks): "A Naked Man Lying On Their Back" reached Live's home (#339)
Changes
@@@ -58,20 +58,23 @@ function _deJson(desc) {// dangling word ("…Speaking To") and never about how someone looks (owner's rule for public AI text: "A Man With A// Beard Is Speaking To" and "A Person In A Bright Yellow High-Visibility Jacket" titled public clips, 2026-10-17).// Otherwise the stream's own title and the moment's time ("Fortnite! at 41:20").--const LOOKS_WORDS = /\b(wearing|wears|dressed|beard(ed)?|mustache|glasses|sunglasses|jacket|hoodie|sweater|vest|shirt|shirtless|tank top|t-shirt|beanie|cap|hat|helmet|headset|headphones|hair|bald|blonde|expression|face|smil(e|ing)|clothing|outfit|tattoo(ed)?|young|old(er)?|teen(age)?|boy|girl|kid|child|blurred|body|skin)\b/i;++const LOOKS_WORDS = /\b(wearing|wears|dressed|beard(ed)?|mustache|glasses|sunglasses|jacket|hoodie|sweater|vest|shirt|shirtless|topless|naked|nude|tank top|t-shirt|beanie|cap|hat|helmet|headset|headphones|hair|bald|blonde|expression|face|smil(e|ing)|clothing|outfit|tattoo(ed)?|young|old(er)?|teen(age)?|boy|girl|kid|child|blurred|body|skin)\b/i;++// A title also fails the shared rule (openvibe-sdk/no-looks, db.hasLooks): this list alone let "A Naked Man Lying On++// Their Back With Legs" through to Live's home (clip 483, 2026-10-23): it named neither "man" nor "naked".++const namesLooks = (s) => LOOKS_WORDS.test(s) || db.hasLooks(s);const DANGLING = /\s+(to|in|with|a|an|of|and|the|on|at|is|are|was|from|for|by|near|while|as|or|into|onto|its|his|her|their)$/i;function _captionTitle(desc) {let s = _cleanText(desc).split(/[.!?;:]|,\s(?=(?:with|and|as|while|near|showing|likely)\b)/i)[0].trim();s = s.split(' ').filter(Boolean).slice(0, 9).join(' ').replace(/[,\s]+$/, '');while (DANGLING.test(s)) s = s.replace(DANGLING, '');-- if (s.split(' ').length < 3 || LOOKS_WORDS.test(s)) return null;++ if (s.split(' ').length < 3 || namesLooks(s)) return null;return s.replace(/\b\w/g, (ch) => ch.toUpperCase()).slice(0, 70);}// An AI-written title is used unless it is about looks ("Red Beanie Moment", "Glasses Adjustment Tension"); then the// fallback above.function _publicTitle(title, vod, offset, desc) {const t = _cleanTitle(title);-- return t && !LOOKS_WORDS.test(t) ? t : _fallbackTitle(vod, offset, desc);++ return t && !namesLooks(t) ? t : _fallbackTitle(vod, offset, desc);}function _fallbackTitle(vod, offset, desc) {const fromCaption = desc ? _captionTitle(desc) : null;@@@ -28,6 +28,10 @@ assert.ok(fallbackTitle({ title: 'x'.repeat(200) }, 59).length <= 80, 'at most 8const { publicTitle } = require('../server/ai/ai-moments-job');assert.strictEqual(publicTitle('Boss Fight Comeback', vod, 60, ''), 'Boss Fight Comeback');assert.strictEqual(publicTitle('Red Beanie Moment', vod, 2480, 'A man wearing a red beanie looks up.'), 'Fortnite! at 41:20');++// The shared rule too (openvibe-sdk/no-looks via db.hasLooks): clip 483 was titled this on Live's home (2026-10-23).++assert.strictEqual(publicTitle('A Naked Man Lying On Their Back With Legs', vod, 2480, ''), 'Fortnite! at 41:20');++assert.strictEqual(publicTitle('Woman Laughs At Chat', vod, 2480, ''), 'Fortnite! at 41:20', 'a guessed gender is a look');++assert.strictEqual(captionTitle('A naked man lies on a bed near a window.'), null);assert.strictEqual(publicTitle('Headphones Hit a Revelation', { title: 'A little couch surfing before the store.' }, 61, ''), 'A little couch surfing before the store at 1:01');assert.strictEqual(publicTitle('', { title: '', username: 'goosely' }, 75, ''), "goosely's stream at 1:15");process.stdout.write('moment fallback title: all checks passed\n');
@@ -58,20 +58,23 @@ function _deJson(desc) {
@@ -58,20 +58,23 @@ function _deJson(desc) {
// dangling word ("…Speaking To") and never about how someone looks (owner's rule for public AI text: "A Man With A
// dangling word ("…Speaking To") and never about how someone looks (owner's rule for public AI text: "A Man With A
// Beard Is Speaking To" and "A Person In A Bright Yellow High-Visibility Jacket" titled public clips, 2026-10-17).
// Beard Is Speaking To" and "A Person In A Bright Yellow High-Visibility Jacket" titled public clips, 2026-10-17).
// Otherwise the stream's own title and the moment's time ("Fortnite! at 41:20").
// Otherwise the stream's own title and the moment's time ("Fortnite! at 41:20").
-const LOOKS_WORDS = /\b(wearing|wears|dressed|beard(ed)?|mustache|glasses|sunglasses|jacket|hoodie|sweater|vest|shirt|shirtless|tank top|t-shirt|beanie|cap|hat|helmet|headset|headphones|hair|bald|blonde|expression|face|smil(e|ing)|clothing|outfit|tattoo(ed)?|young|old(er)?|teen(age)?|boy|girl|kid|child|blurred|body|skin)\b/i;
+const LOOKS_WORDS = /\b(wearing|wears|dressed|beard(ed)?|mustache|glasses|sunglasses|jacket|hoodie|sweater|vest|shirt|shirtless|topless|naked|nude|tank top|t-shirt|beanie|cap|hat|helmet|headset|headphones|hair|bald|blonde|expression|face|smil(e|ing)|clothing|outfit|tattoo(ed)?|young|old(er)?|teen(age)?|boy|girl|kid|child|blurred|body|skin)\b/i;
+// A title also fails the shared rule (openvibe-sdk/no-looks, db.hasLooks): this list alone let "A Naked Man Lying On
+// Their Back With Legs" through to Live's home (clip 483, 2026-10-23): it named neither "man" nor "naked".
+const namesLooks = (s) => LOOKS_WORDS.test(s) || db.hasLooks(s);
const DANGLING = /\s+(to|in|with|a|an|of|and|the|on|at|is|are|was|from|for|by|near|while|as|or|into|onto|its|his|her|their)$/i;
const DANGLING = /\s+(to|in|with|a|an|of|and|the|on|at|is|are|was|from|for|by|near|while|as|or|into|onto|its|his|her|their)$/i;
function _captionTitle(desc) {
function _captionTitle(desc) {
let s = _cleanText(desc).split(/[.!?;:]|,\s(?=(?:with|and|as|while|near|showing|likely)\b)/i)[0].trim();
let s = _cleanText(desc).split(/[.!?;:]|,\s(?=(?:with|and|as|while|near|showing|likely)\b)/i)[0].trim();
s = s.split(' ').filter(Boolean).slice(0, 9).join(' ').replace(/[,\s]+$/, '');
s = s.split(' ').filter(Boolean).slice(0, 9).join(' ').replace(/[,\s]+$/, '');
while (DANGLING.test(s)) s = s.replace(DANGLING, '');
while (DANGLING.test(s)) s = s.replace(DANGLING, '');
- if (s.split(' ').length < 3 || LOOKS_WORDS.test(s)) return null;
+ if (s.split(' ').length < 3 || namesLooks(s)) return null;
return s.replace(/\b\w/g, (ch) => ch.toUpperCase()).slice(0, 70);
return s.replace(/\b\w/g, (ch) => ch.toUpperCase()).slice(0, 70);
}
}
// An AI-written title is used unless it is about looks ("Red Beanie Moment", "Glasses Adjustment Tension"); then the
// An AI-written title is used unless it is about looks ("Red Beanie Moment", "Glasses Adjustment Tension"); then the
// fallback above.
// fallback above.
function _publicTitle(title, vod, offset, desc) {
function _publicTitle(title, vod, offset, desc) {
const t = _cleanTitle(title);
const t = _cleanTitle(title);
- return t && !LOOKS_WORDS.test(t) ? t : _fallbackTitle(vod, offset, desc);
+ return t && !namesLooks(t) ? t : _fallbackTitle(vod, offset, desc);
}
}
function _fallbackTitle(vod, offset, desc) {
function _fallbackTitle(vod, offset, desc) {
const fromCaption = desc ? _captionTitle(desc) : null;
const fromCaption = desc ? _captionTitle(desc) : null;
@@ -28,6 +28,10 @@ assert.ok(fallbackTitle({ title: 'x'.repeat(200) }, 59).length <= 80, 'at most 8
@@ -28,6 +28,10 @@ assert.ok(fallbackTitle({ title: 'x'.repeat(200) }, 59).length <= 80, 'at most 8
const { publicTitle } = require('../server/ai/ai-moments-job');
const { publicTitle } = require('../server/ai/ai-moments-job');
assert.strictEqual(publicTitle('Boss Fight Comeback', vod, 60, ''), 'Boss Fight Comeback');
assert.strictEqual(publicTitle('Boss Fight Comeback', vod, 60, ''), 'Boss Fight Comeback');
assert.strictEqual(publicTitle('Red Beanie Moment', vod, 2480, 'A man wearing a red beanie looks up.'), 'Fortnite! at 41:20');
assert.strictEqual(publicTitle('Red Beanie Moment', vod, 2480, 'A man wearing a red beanie looks up.'), 'Fortnite! at 41:20');
+// The shared rule too (openvibe-sdk/no-looks via db.hasLooks): clip 483 was titled this on Live's home (2026-10-23).
+assert.strictEqual(publicTitle('A Naked Man Lying On Their Back With Legs', vod, 2480, ''), 'Fortnite! at 41:20');
+assert.strictEqual(publicTitle('Woman Laughs At Chat', vod, 2480, ''), 'Fortnite! at 41:20', 'a guessed gender is a look');
+assert.strictEqual(captionTitle('A naked man lies on a bed near a window.'), null);
assert.strictEqual(publicTitle('Headphones Hit a Revelation', { title: 'A little couch surfing before the store.' }, 61, ''), 'A little couch surfing before the store at 1:01');
assert.strictEqual(publicTitle('Headphones Hit a Revelation', { title: 'A little couch surfing before the store.' }, 61, ''), 'A little couch surfing before the store at 1:01');
assert.strictEqual(publicTitle('', { title: '', username: 'goosely' }, 75, ''), "goosely's stream at 1:15");
assert.strictEqual(publicTitle('', { title: '', username: 'goosely' }, 75, ''), "goosely's stream at 1:15");
process.stdout.write('moment fallback title: all checks passed\n');
process.stdout.write('moment fallback title: all checks passed\n');