openvibers/openvibe.live Commits

The looks repair scans card lines on their own too: 15 clip card lines with no full text still described people after #298 (#299)

OP OpenVibers committed 22 days ago · 1ae82b58af · 2 files, +21 −1

Changes

ddiff --git a/server/db/database.js b/server/db/database.js
iindex 02a1cd8f..fcbe8745 100644
---- a/server/db/database.js
++++ b/server/db/database.js
@@@ -735,8 +735,10 @@ function _extractDescFromMaybeJson(text) {
// The looks repair below scans ~20k rows with a regex (~0.8 s of database time on every boot). New rows are scrubbed when
// written, so the scan only needs to run again when the scrub rules change: this hash of their source says when.
let _looksRulesVersion = null;
++// What the repair covers is part of its version: a change in scope runs the scan again even when the rules did not change.
++const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own (2026-10-22)';
function looksRulesVersion() {
-- if (!_looksRulesVersion) _looksRulesVersion = require('crypto').createHash('sha1').update([_noLooks, _noLooksWords, _noGender, _noGrooming, _noClothing, _LOOKS, _PEOPLE, _WORN, _WORN_ANY, _FACE, _GENDER_ADJ, _GROOMING].map(String).join('|')).digest('hex').slice(0, 12);
++ if (!_looksRulesVersion) _looksRulesVersion = require('crypto').createHash('sha1').update([_LOOKS_REPAIR_SCOPE, _noLooks, _noLooksWords, _noGender, _noGrooming, _noClothing, _LOOKS, _PEOPLE, _WORN, _WORN_ANY, _FACE, _GENDER_ADJ, _GROOMING].map(String).join('|')).digest('hex').slice(0, 12);
return _looksRulesVersion;
}
async function cleanupMalformedAiText({ force = false } = {}) {
@@@ -796,6 +798,17 @@ async function cleanupMalformedAiText({ force = false } = {}) {
else await run(`UPDATE ${table} SET ${col} = ? WHERE ${key} = ?`, [clean, r.k]);
fixed++;
}
++ // A card line can be stored without its full text (or derived from an older one): it is repaired on its own.
++ // ' ' is the "nothing to say" sentinel and stays when the whole line goes.
++ if (short) {
++ const shorts = await all(`SELECT ${key} AS k, ${short} AS v FROM ${table} WHERE ${short} ${looks}`);
++ for (const r of shorts) {
++ const clean = _noLooks(r.v);
++ if (clean === r.v) continue;
++ await run(`UPDATE ${table} SET ${short} = ? WHERE ${key} = ?`, [clean && clean.trim() ? clean : ' ', r.k]);
++ fixed++;
++ }
++ }
} catch (e) { console.warn(`[AI] looks repair (${table}):`, e.message); looksDone = null; }
}
// Recorded only after a clean pass, so a failed table is tried again at the next boot.
ddiff --git a/test/ai-text-no-looks.test.js b/test/ai-text-no-looks.test.js
iindex 060c8e09..71e465aa 100644
---- a/test/ai-text-no-looks.test.js
++++ b/test/ai-text-no-looks.test.js
@@@ -90,6 +90,13 @@ const db = require('../server/db/database');
await db.setState('ai_looks_repair_rules', 'an older rule set');
await db.cleanupMalformedAiText();
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9240')).description, 'A person cooks pasta.');
++ // A card line stored without its full text (or derived from an older one) is repaired on its own.
++ await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990001, 'The video shows a man wearing a headset in a kitchen.']);
++ await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990002, 'A person adjusts their hair or clothing near the counter.']);
++ await db.setState('ai_looks_repair_rules', 'an older rule set');
++ await db.cleanupMalformedAiText();
++ assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990001')).ai_overview_short, 'The video shows a person in a kitchen.');
++ assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990002')).ai_overview_short, ' ', 'a line that is all looks becomes the nothing-to-say sentinel');
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests his chin on his hand.');
assert.deepStrictEqual(Object.values(await db.get('SELECT ai_overview, ai_overview_short FROM streams WHERE id = ?', [sid])), ['The streamer plays Fortnite.', 'The streamer plays Fortnite.']);
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9060')).description, 'An older version of the game loads.', 'looks words about things stay');