openvibers/openvibe.live Commits

No 'free' or $0 in Live's home metadata (owner copy rule; audit 32.2 row 2779): the server-rendered home title, description, heading and Organization description say open-source and community run instead, and the shell's SoftwareApplication JSON-LD drops its offers.price '0'. test/seo-ssr.test.js checks the home head and body (free speech stays fine).

OP OpenVibers committed 24 Sept 2026 · b6d1c9b6bc · 3 files, +14 −10


Changes

ddiff --git a/public/index.html b/public/index.html
iindex 676c4514..63c6b9e3 100644
---- a/public/index.html
++++ b/public/index.html
@@@ -136,12 +136,7 @@
"description": "Open source live streaming platform with restreaming, VODs, clips, and 200+ community tools",
"url": "https://openvibe.live",
"applicationCategory": "Multimedia",
-- "operatingSystem": "Web",
-- "offers": {
-- "@type": "Offer",
-- "price": "0",
-- "priceCurrency": "USD"
-- }
++ "operatingSystem": "Web"
}
</script>
<script type="application/ld+json">
ddiff --git a/server/seo/seo.js b/server/seo/seo.js
iindex 8c61ae1c..9e7f3ad9 100644
---- a/server/seo/seo.js
++++ b/server/seo/seo.js
@@@ -424,8 +424,8 @@ function _mediaSection(heading, items) {
function _fmtDur(sec) { sec = Math.floor(Number(sec) || 0); const m = Math.floor(sec / 60), s = sec % 60; return sec ? `${m}:${String(s).padStart(2, '0')}` : ''; }
async function _homeMeta() {
-- const title = 'OpenVibe.Live — Free Open-Source Live Streaming Platform';
-- const description = 'Free, open-source live streaming with sub-second WebRTC, OBS/RTMP & CLI ingest, auto VODs & AI clips, restreaming to Twitch/YouTube/Kick, global chat, viewer-controlled robots, and the whole OpenVibe network behind it.';
++ const title = 'OpenVibe.Live — Open-Source Live Streaming, Community Run';
++ const description = 'Open-source, community-run live streaming with sub-second WebRTC, OBS/RTMP & CLI ingest, auto VODs & AI clips, restreaming to Twitch/YouTube/Kick, global chat, viewer-controlled robots, and the whole OpenVibe network behind it.';
// Pull the actual live content so the source has real, crawlable text.
let live = [];
@@@ -450,7 +450,7 @@ async function _homeMeta() {
const statLine = stats ? `<p>${SITE_NAME} hosts ${stats.streamers || 0} streamers, ${stats.vods || 0} VODs, ${stats.clips || 0} clips, ${stats.pastes || 0} pastes and ${stats.chatMessages || 0} chat messages.</p>` : '';
const snapshot =
-- `<h1>${SITE_NAME} — free, open-source live streaming</h1>` +
++ `<h1>${SITE_NAME} — open-source live streaming, community run</h1>` +
`<p>${esc(description)}</p>` + statLine +
(liveItems.length ? _mediaSection('Live now', liveItems) : '<section><h2>Live now</h2><p>No one is streaming right now — be the first to go live.</p></section>') +
_mediaSection('Recent VODs', vodItems) +
@@@ -469,7 +469,7 @@ async function _homeMeta() {
{
'@context': 'https://schema.org', '@type': 'Organization', '@id': `${baseUrl()}/#org`,
name: 'OpenVibe', url: baseUrl(), logo: DEFAULT_OG_IMAGE,
-- description: 'A free, open-source network of live streaming, media and developer tools, built and run by the people who use it.',
++ description: 'An open-source network of live streaming, media and developer tools, built and run by the people who use it.',
// sameAs is how a search engine learns these properties are one brand rather than
// unrelated sites that happen to share a name.
sameAs: [
ddiff --git a/test/seo-ssr.test.js b/test/seo-ssr.test.js
iindex cd3e21b7..8043e714 100644
---- a/test/seo-ssr.test.js
++++ b/test/seo-ssr.test.js
@@@ -283,6 +283,15 @@ const server = http.createServer(app).listen(0, '127.0.0.1', async () => {
assert.ok(/max-age=30\b/.test(r.cache), r.cache);
});
++ await check('the home page\'s metadata says no "free" or "$0" (owner copy rule; free speech is fine)', async () => {
++ const html = (await get('/')).body;
++ const meta = head(html) + bodyOf(html);
++ const hits = (meta.match(/[^.<>"]{0,30}\bfree\b[^.<>"]{0,30}/gi) || []).filter((m) => !/free speech/i.test(m));
++ assert.deepStrictEqual(hits, [], 'free copy in the home metadata');
++ assert.ok(!/"price"\s*:/.test(head(html)), 'no price in the structured data');
++ assert.ok(!/\$0\b/.test(meta));
++ });
++
await check('server/index.js sends the fallback shell through seo.shellHtml', () => {
const src = fs.readFileSync(path.join(__dirname, '../server/index.js'), 'utf8');
assert.ok(src.includes("require('./seo/seo').shellHtml"));