openvibers/openvibe.mediahub Commits
Video and Pics homes say what the site is in structured data (WebSite + WebPage JSON-LD, as Download's home does) (#96)
Changes
@@@ -205,6 +205,11 @@ function createPicsRoutes(ctx) {return page(req, res, {index: true,description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.',++ // What the site is, for search engines (as the Download home says it).++ jsonLd: [++ seo.jsonLd.website({ name: SITE_NAME, url: base, description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.' }),++ seo.jsonLd.webPage({ name: SITE_NAME, url: `${base}/`, description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.', siteUrl: base }),++ ],scripts: canUpload ? ['js/pics.js'] : [],body: html`<header class="ovp-hero"><p class="ovp-eyebrow">${SITE_NAME}</p><h1>Share pictures. <span class="ovp-accent">Fast, open, yours.</span></h1>@@@ -695,6 +695,11 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textarreturn page(req, res, {index: true,description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.',++ // What the site is, for search engines (as the Download home says it).++ jsonLd: [++ seo.jsonLd.website({ name: 'OpenVibe.Video', url: base, description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.' }),++ seo.jsonLd.webPage({ name: 'OpenVibe.Video', url: `${base}/`, description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.', siteUrl: base }),++ ],body: html`<header class="ovv-hero"><p class="ovv-eyebrow"><i class="fa-solid fa-circle-play" aria-hidden="true"></i> OpenVibe.Video</p><h1>Watch, upload, stream.</h1><p class="ovv-lede">Upload a video or record your camera or screen right here, and OpenVibe.AI adds captions, chapters and a summary you can use as the description. The recordings and clips of streams on OpenVibe.Live are here too. Everything you post plays on your channel at openvibe.video/@you.</p>@@@ -70,6 +70,7 @@ function multipart(fields, file) {const home = await t.get('/', { headers: PICS });assert.strictEqual(home.status, 200);assert.ok(home.text.includes('Share pictures.') && home.text.includes('Sign in to upload'));++ assert.ok(/<script type="application\/ld\+json">[^<]*"@type":"WebSite"/.test(home.text), 'the home says what the site is (JSON-LD)');assert.strictEqual((await t.get('/files', { headers: PICS, as: kim })).status, 404, 'no drive pages on the pics host');assert.ok((await t.get('/', { headers: { Host: 'openvibe.download' } })).text.indexOf('Share pictures.') === -1, 'the drive host is not Pics');assert.strictEqual((await t.get('/robots.txt', { headers: PICS })).text.includes('Disallow: /i/'), true, 'pictures are not crawled');@@@ -242,6 +242,8 @@ function countOccurrences(haystack, needle) {assert.ok(r.text.includes('Latest recordings'));assert.ok(r.text.includes('Latest clips'));assert.ok(r.text.includes('/v/101'));++ const ld = [...r.text.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].flatMap((m) => [].concat(JSON.parse(m[1])));++ assert.ok(ld.some((n) => n['@type'] === 'WebSite' && n.name === 'OpenVibe.Video'), 'the home says what the site is (JSON-LD)');assert.ok(r.text.includes('/c/201'));assert.ok(!r.text.includes('/v/102'), 'the unlisted VOD is not in a list');assert.ok(!r.text.includes('/v/109'), 'a recording under 15 seconds gets no tile on the front page');
@@ -205,6 +205,11 @@ function createPicsRoutes(ctx) {
@@ -205,6 +205,11 @@ function createPicsRoutes(ctx) {
return page(req, res, {
return page(req, res, {
index: true,
index: true,
description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.',
description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.',
+ // What the site is, for search engines (as the Download home says it).
+ jsonLd: [
+ seo.jsonLd.website({ name: SITE_NAME, url: base, description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.' }),
+ seo.jsonLd.webPage({ name: SITE_NAME, url: `${base}/`, description: 'Upload a picture and get a link to share or embed anywhere. Albums, no ads, open source, with location and camera details removed from every picture.', siteUrl: base }),
+ ],
scripts: canUpload ? ['js/pics.js'] : [],
scripts: canUpload ? ['js/pics.js'] : [],
body: html`<header class="ovp-hero"><p class="ovp-eyebrow">${SITE_NAME}</p>
body: html`<header class="ovp-hero"><p class="ovp-eyebrow">${SITE_NAME}</p>
<h1>Share pictures. <span class="ovp-accent">Fast, open, yours.</span></h1>
<h1>Share pictures. <span class="ovp-accent">Fast, open, yours.</span></h1>
@@ -695,6 +695,11 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar
@@ -695,6 +695,11 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar
return page(req, res, {
return page(req, res, {
index: true,
index: true,
description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.',
description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.',
+ // What the site is, for search engines (as the Download home says it).
+ jsonLd: [
+ seo.jsonLd.website({ name: 'OpenVibe.Video', url: base, description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.' }),
+ seo.jsonLd.webPage({ name: 'OpenVibe.Video', url: `${base}/`, description: 'Videos people upload, and stream recordings and clips from OpenVibe.Live with AI chapters and captions.', siteUrl: base }),
+ ],
body: html`<header class="ovv-hero"><p class="ovv-eyebrow"><i class="fa-solid fa-circle-play" aria-hidden="true"></i> OpenVibe.Video</p>
body: html`<header class="ovv-hero"><p class="ovv-eyebrow"><i class="fa-solid fa-circle-play" aria-hidden="true"></i> OpenVibe.Video</p>
<h1>Watch, upload, stream.</h1>
<h1>Watch, upload, stream.</h1>
<p class="ovv-lede">Upload a video or record your camera or screen right here, and OpenVibe.AI adds captions, chapters and a summary you can use as the description. The recordings and clips of streams on OpenVibe.Live are here too. Everything you post plays on your channel at openvibe.video/@you.</p>
<p class="ovv-lede">Upload a video or record your camera or screen right here, and OpenVibe.AI adds captions, chapters and a summary you can use as the description. The recordings and clips of streams on OpenVibe.Live are here too. Everything you post plays on your channel at openvibe.video/@you.</p>
@@ -70,6 +70,7 @@ function multipart(fields, file) {
@@ -70,6 +70,7 @@ function multipart(fields, file) {
const home = await t.get('/', { headers: PICS });
const home = await t.get('/', { headers: PICS });
assert.strictEqual(home.status, 200);
assert.strictEqual(home.status, 200);
assert.ok(home.text.includes('Share pictures.') && home.text.includes('Sign in to upload'));
assert.ok(home.text.includes('Share pictures.') && home.text.includes('Sign in to upload'));
+ assert.ok(/<script type="application\/ld\+json">[^<]*"@type":"WebSite"/.test(home.text), 'the home says what the site is (JSON-LD)');
assert.strictEqual((await t.get('/files', { headers: PICS, as: kim })).status, 404, 'no drive pages on the pics host');
assert.strictEqual((await t.get('/files', { headers: PICS, as: kim })).status, 404, 'no drive pages on the pics host');
assert.ok((await t.get('/', { headers: { Host: 'openvibe.download' } })).text.indexOf('Share pictures.') === -1, 'the drive host is not Pics');
assert.ok((await t.get('/', { headers: { Host: 'openvibe.download' } })).text.indexOf('Share pictures.') === -1, 'the drive host is not Pics');
assert.strictEqual((await t.get('/robots.txt', { headers: PICS })).text.includes('Disallow: /i/'), true, 'pictures are not crawled');
assert.strictEqual((await t.get('/robots.txt', { headers: PICS })).text.includes('Disallow: /i/'), true, 'pictures are not crawled');
@@ -242,6 +242,8 @@ function countOccurrences(haystack, needle) {
@@ -242,6 +242,8 @@ function countOccurrences(haystack, needle) {
assert.ok(r.text.includes('Latest recordings'));
assert.ok(r.text.includes('Latest recordings'));
assert.ok(r.text.includes('Latest clips'));
assert.ok(r.text.includes('Latest clips'));
assert.ok(r.text.includes('/v/101'));
assert.ok(r.text.includes('/v/101'));
+ const ld = [...r.text.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].flatMap((m) => [].concat(JSON.parse(m[1])));
+ assert.ok(ld.some((n) => n['@type'] === 'WebSite' && n.name === 'OpenVibe.Video'), 'the home says what the site is (JSON-LD)');
assert.ok(r.text.includes('/c/201'));
assert.ok(r.text.includes('/c/201'));
assert.ok(!r.text.includes('/v/102'), 'the unlisted VOD is not in a list');
assert.ok(!r.text.includes('/v/102'), 'the unlisted VOD is not in a list');
assert.ok(!r.text.includes('/v/109'), 'a recording under 15 seconds gets no tile on the front page');
assert.ok(!r.text.includes('/v/109'), 'a recording under 15 seconds gets no tile on the front page');