openvibers/openvibe.mediahub Commits
Video, Pics and Download copy no longer advertises "no ads" (the network never promises it); Pics' title says it shares and embeds pictures (#107)
Changes
@@@ -151,7 +151,7 @@ ${notice(p && p.detail ? p.detail : 'You are over the limit for this.', 'warn')}// from config or rules, so the page cannot promise more than the service does.const gb = (b) => rules.formatBytes(b).replace(/\.0 /, ' '); // "2 GB", not "2.0 GB"const homeFaq = () => [-- { q: 'Is OpenVibe.Download free?', a: `Yes. A free OpenVibe account gets ${gb(config.quotas.bytes)} of storage, files up to ${gb(config.quotas.maxFileBytes)} each and ${config.quotas.uploadsPerDay} uploads a day. There are no ads.` },++ { q: 'Is OpenVibe.Download free?', a: `Yes. A free OpenVibe account gets ${gb(config.quotas.bytes)} of storage, files up to ${gb(config.quotas.maxFileBytes)} each and ${config.quotas.uploadsPerDay} uploads a day.` },{ q: 'Who can open a file I share?', a: `Only the people you choose: named OpenVibe accounts, anyone signed in, or anyone with the link on a public download page. Every link expires in an hour to ${rules.EXPIRY_MAX_HOURS / 24} days, and you can revoke it at any time.` },{ q: 'How do backups work?', a: 'Make a sync key on the Backups page and run the openvibe-download command on your computer, once or on a schedule. It sends only new or changed files into a folder of your drive, and it never deletes anything there unless you ask it to.' },drops.enabled()@@@ -170,7 +170,7 @@ ${notice(p && p.detail ? p.detail : 'You are over the limit for this.', 'warn')}actions: signedIn(req)? [{ label: 'Your files', href: '/files', primary: true }, { label: 'Back up a folder', href: '/sync' }]: [{ label: 'Sign in with OpenVibe', href: '/auth/login?next=%2Ffiles', primary: true }, { label: 'How backups work', href: '#sc-back-up-a-folder-from-your-computer' }],-- note: 'Open source, no ads. The bytes live in OpenVibe.Media; this service holds the names, the folders and the share links.',++ note: 'Open source. The bytes live in OpenVibe.Media; this service holds the names, the folders and the share links.',});const faq = homeFaq();page(req, res, {@@@ -229,7 +229,7 @@ ${raw(showcase.nextSteps({title: 'More on OpenVibe',items: [{ to: 'video', what: 'Upload a video', text: 'With AI captions and chapters, on your own channel.', href: `${config.video.url}/`, from: 'download' },-- { to: 'pics', what: 'Share a picture', text: 'A link to share or embed anywhere, no ads.', href: `${config.pics.url}/`, from: 'download' },++ { to: 'pics', what: 'Share a picture', text: 'A link to share or embed anywhere.', href: `${config.pics.url}/`, from: 'download' },{ to: 'live', what: 'Go live from your browser', text: 'No OBS and no follower minimum.', href: 'https://openvibe.live/', from: 'download' },{ to: 'chat', what: 'Talk it over', text: 'Rooms, servers and voice channels.', href: 'https://openvibe.chat/', from: 'download' },],@@@ -205,7 +205,7 @@ function createPicsRoutes(ctx) {const FORMAT_NAMES = 'JPEG, PNG, GIF, WebP or AVIF';const sizeText = (b) => formatBytes(b).replace(/\.0 /, ' ');const homeFaq = () => [-- { q: 'Is OpenVibe.Pics free?', a: `Yes, and there are no ads. A free OpenVibe account can upload ${cfg.imagesPerDay} pictures a day, each up to ${sizeText(cfg.maxImageBytes)}, and keep up to ${sizeText(cfg.quotaBytes)}.` },++ { q: 'Is OpenVibe.Pics free?', a: `Yes. A free OpenVibe account can upload ${cfg.imagesPerDay} pictures a day, each up to ${sizeText(cfg.maxImageBytes)}, and keep up to ${sizeText(cfg.quotaBytes)}.` },{ q: 'Who can see my pictures?', a: 'Only people you give the link to. Pictures are never listed, ranked or shown to search engines, and you can delete one at any time.' },{ q: 'What is removed from my pictures?', a: 'Everything hidden in the file: the GPS position, the camera\'s serial number and the rest of its metadata. Every picture is decoded and saved again before anyone sees it.' },{ q: 'Which files can I upload?', a: `${FORMAT_NAMES}, animated GIF and WebP included. SVG and other formats are refused, so nothing is ever served as it was uploaded.` },@@@ -215,11 +215,11 @@ function createPicsRoutes(ctx) {r.get('/', async (req, res) => {const mine = signedIn(req) ? (await store.imagesOf(s, whoOf(req).requester, { limit: 12 })).rows : [];const canUpload = signedIn(req) || cfg.anonymous;-- const description = 'Upload a picture and get a link to share or embed anywhere. Albums, any size or format, no ads, with location and camera details removed.';++ const description = 'Upload a picture and get a link to share or embed anywhere: albums, any size or format, and location and camera details removed from every picture.';const faq = homeFaq();return page(req, res, {index: true,-- title: `${SITE_NAME}: share pictures with a link, no ads`,++ title: `${SITE_NAME}: share and embed pictures with a link`,description,jsonLd: [seo.jsonLd.website({ name: SITE_NAME, url: base, description }),@@@ -230,7 +230,7 @@ function createPicsRoutes(ctx) {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>--<p class="ovp-lede">Drop a picture, get a link to share or embed anywhere: on a forum, in a chat, on your site. No ads, no feed, and nothing about you hidden in the file.</p></header>++<p class="ovp-lede">Drop a picture, get a link to share or embed anywhere: on a forum, in a chat, on your site. No feed to scroll, and nothing about you hidden in the file.</p></header>${canUpload ? uploadForm(req) : html`<section class="ovp-drop ovp-drop--signin"><div class="ovp-drop-inner"><p class="ovp-drop-title">Sign in to upload</p><p class="ovp-drop-hint">Your OpenVibe account keeps your pictures and albums together, so you can delete them whenever you like.</p><p>${signInLink('/')}</p></div></section>`}${signedIn(req) ? html`<section class="ovp-section" aria-labelledby="h-latest"><div class="ovp-section-head"><h2 id="h-latest">Your latest pictures</h2><a href="/mine">All of them</a></div>${grid(mine, 'Nothing yet: your uploads show up here.')}</section>` : ''}<section class="ovp-section" aria-labelledby="h-how"><h2 id="h-how">How it works</h2><ol class="ovp-steps">@@@ -579,7 +579,7 @@ ${row.state === 'hidden' ? html`<button class="ovp-btn" name="action" value="res.send(seo.sitemapXml(['/', '/updates', ...legal.PATHS].map((p) => ({ loc: `${base}${p}` })))));r.get('/llms.txt', (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 })).send(seo.llmsTxt({name: SITE_NAME,-- summary: 'Picture hosting on the OpenVibe network: upload a picture, get a link to share or embed, albums, no ads.',++ summary: 'Picture hosting on the OpenVibe network: upload a picture, get a link to share or embed, albums, any size or format.',details: 'Pictures are unlisted (reachable only by their link). Each one is re-encoded with its metadata removed, and can be had resized and converted at /i/<slug>/w/<320|640|1280|1920|2560>.<webp|avif|jpg|png>. Alt text: the uploader writes it or asks OpenVibe.AI for a draft (POST /api/v1/images/<slug>/describe). API: POST /api/v1/images with the picture as the body (an OpenVibe sign-in token as a Bearer).',sections: [{ title: 'Start here', links: [{ title: SITE_NAME, url: `${base}/` }, { title: 'Terms', url: `${base}/terms` }, { title: 'DMCA', url: `${base}/dmca` }] }],})));@@@ -780,7 +780,7 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar}const n = (v) => Number(v).toLocaleString('en-US');const HOME_FAQ = (vu) => [-- { q: 'Is OpenVibe.Video free?', a: `Yes. Watching needs no account, and uploading needs a free OpenVibe account. Each person can upload ${vu.perDay} videos a day and keep up to ${formatBytes(vu.quotaBytes)}. There are no ads.` },++ { q: 'Is OpenVibe.Video free?', a: `Yes. Watching needs no account, and uploading needs a free OpenVibe account. Each person can upload ${vu.perDay} videos a day and keep up to ${formatBytes(vu.quotaBytes)}.` },{ q: 'Which files can I upload?', a: `MP4 (MOV and M4V too), WebM and MKV, up to ${formatBytes(vu.maxBytes)} each. You can also record your camera or screen right in the browser.` },{ q: 'How do streams from OpenVibe.Live get here?', a: 'When a stream on OpenVibe.Live is recorded, the recording appears here after the stream ends, on the streamer\'s channel at openvibe.video/@name, together with the clips people cut from it.' },{ q: 'Where do the captions and chapters come from?', a: 'OpenVibe.AI turns the speech into captions, splits the video into chapters and writes a short summary. On your own uploads you can use the summary as the description.' },@@@ -853,7 +853,7 @@ ${channels.length ? html`<section class="ovv-section" aria-labelledby="h-channel<section class="ovv-section" aria-labelledby="h-faq"><h2 id="h-faq">Questions</h2><div class="ovv-faq">${faq.map((f) => html`<details><summary>${f.q}</summary><p>${f.a}</p></details>`)}</div></section><section class="ovv-section" aria-labelledby="h-more"><h2 id="h-more">More on OpenVibe</h2><ul class="ovv-more" role="list"><li><a href="${livePublic}/"><strong>OpenVibe.Live</strong><span>Go live from your browser, no OBS needed.</span></a></li>--<li><a href="${config.pics.url}/"><strong>OpenVibe.Pics</strong><span>Share pictures with a link, no ads.</span></a></li>++<li><a href="${config.pics.url}/"><strong>OpenVibe.Pics</strong><span>Share and embed pictures with a link.</span></a></li><li><a href="${config.baseUrl}/"><strong>OpenVibe.Download</strong><span>Your files and backups, shared on your terms.</span></a></li><li><a href="https://openvibe.community/"><strong>OpenVibe.Community</strong><span>Forums and pastes for talking it over.</span></a></li></ul></section>`,@@@ -79,7 +79,7 @@ function multipart(fields, file) {await check('the home: how it works, the links, the API, the limits from config, questions that match its FAQPage', async () => {const home = (await t.get('/', { headers: PICS })).text;assert.strictEqual((home.match(/<h1[\s>]/g) || []).length, 1, 'one h1');-- assert.match(home, /<title>OpenVibe\.Pics: share pictures with a link, no ads<\/title>/);++ assert.match(home, /<title>OpenVibe\.Pics: share and embed pictures with a link<\/title>/);const sections = ['How it works', 'The links a picture gets', 'Upload from your own app', 'What a free account gets', 'Questions', 'More on OpenVibe'];assert.deepStrictEqual(sections.filter((h) => !home.includes(`>${h}</h2>`)), [], 'every section is there');assert.ok(home.includes('<td>20 MB</td>') && home.includes('<td>200</td>') && home.includes('<td>5 GB</td>'), 'the limits are the configured ones');
@@ -151,7 +151,7 @@ ${notice(p && p.detail ? p.detail : 'You are over the limit for this.', 'warn')}
@@ -151,7 +151,7 @@ ${notice(p && p.detail ? p.detail : 'You are over the limit for this.', 'warn')}
// from config or rules, so the page cannot promise more than the service does.
// from config or rules, so the page cannot promise more than the service does.
const gb = (b) => rules.formatBytes(b).replace(/\.0 /, ' '); // "2 GB", not "2.0 GB"
const gb = (b) => rules.formatBytes(b).replace(/\.0 /, ' '); // "2 GB", not "2.0 GB"
const homeFaq = () => [
const homeFaq = () => [
- { q: 'Is OpenVibe.Download free?', a: `Yes. A free OpenVibe account gets ${gb(config.quotas.bytes)} of storage, files up to ${gb(config.quotas.maxFileBytes)} each and ${config.quotas.uploadsPerDay} uploads a day. There are no ads.` },
+ { q: 'Is OpenVibe.Download free?', a: `Yes. A free OpenVibe account gets ${gb(config.quotas.bytes)} of storage, files up to ${gb(config.quotas.maxFileBytes)} each and ${config.quotas.uploadsPerDay} uploads a day.` },
{ q: 'Who can open a file I share?', a: `Only the people you choose: named OpenVibe accounts, anyone signed in, or anyone with the link on a public download page. Every link expires in an hour to ${rules.EXPIRY_MAX_HOURS / 24} days, and you can revoke it at any time.` },
{ q: 'Who can open a file I share?', a: `Only the people you choose: named OpenVibe accounts, anyone signed in, or anyone with the link on a public download page. Every link expires in an hour to ${rules.EXPIRY_MAX_HOURS / 24} days, and you can revoke it at any time.` },
{ q: 'How do backups work?', a: 'Make a sync key on the Backups page and run the openvibe-download command on your computer, once or on a schedule. It sends only new or changed files into a folder of your drive, and it never deletes anything there unless you ask it to.' },
{ q: 'How do backups work?', a: 'Make a sync key on the Backups page and run the openvibe-download command on your computer, once or on a schedule. It sends only new or changed files into a folder of your drive, and it never deletes anything there unless you ask it to.' },
drops.enabled()
drops.enabled()
@@ -170,7 +170,7 @@ ${notice(p && p.detail ? p.detail : 'You are over the limit for this.', 'warn')}
@@ -170,7 +170,7 @@ ${notice(p && p.detail ? p.detail : 'You are over the limit for this.', 'warn')}
actions: signedIn(req)
actions: signedIn(req)
? [{ label: 'Your files', href: '/files', primary: true }, { label: 'Back up a folder', href: '/sync' }]
? [{ label: 'Your files', href: '/files', primary: true }, { label: 'Back up a folder', href: '/sync' }]
: [{ label: 'Sign in with OpenVibe', href: '/auth/login?next=%2Ffiles', primary: true }, { label: 'How backups work', href: '#sc-back-up-a-folder-from-your-computer' }],
: [{ label: 'Sign in with OpenVibe', href: '/auth/login?next=%2Ffiles', primary: true }, { label: 'How backups work', href: '#sc-back-up-a-folder-from-your-computer' }],
- note: 'Open source, no ads. The bytes live in OpenVibe.Media; this service holds the names, the folders and the share links.',
+ note: 'Open source. The bytes live in OpenVibe.Media; this service holds the names, the folders and the share links.',
});
});
const faq = homeFaq();
const faq = homeFaq();
page(req, res, {
page(req, res, {
@@ -229,7 +229,7 @@ ${raw(showcase.nextSteps({
@@ -229,7 +229,7 @@ ${raw(showcase.nextSteps({
title: 'More on OpenVibe',
title: 'More on OpenVibe',
items: [
items: [
{ to: 'video', what: 'Upload a video', text: 'With AI captions and chapters, on your own channel.', href: `${config.video.url}/`, from: 'download' },
{ to: 'video', what: 'Upload a video', text: 'With AI captions and chapters, on your own channel.', href: `${config.video.url}/`, from: 'download' },
- { to: 'pics', what: 'Share a picture', text: 'A link to share or embed anywhere, no ads.', href: `${config.pics.url}/`, from: 'download' },
+ { to: 'pics', what: 'Share a picture', text: 'A link to share or embed anywhere.', href: `${config.pics.url}/`, from: 'download' },
{ to: 'live', what: 'Go live from your browser', text: 'No OBS and no follower minimum.', href: 'https://openvibe.live/', from: 'download' },
{ to: 'live', what: 'Go live from your browser', text: 'No OBS and no follower minimum.', href: 'https://openvibe.live/', from: 'download' },
{ to: 'chat', what: 'Talk it over', text: 'Rooms, servers and voice channels.', href: 'https://openvibe.chat/', from: 'download' },
{ to: 'chat', what: 'Talk it over', text: 'Rooms, servers and voice channels.', href: 'https://openvibe.chat/', from: 'download' },
],
],
@@ -205,7 +205,7 @@ function createPicsRoutes(ctx) {
@@ -205,7 +205,7 @@ function createPicsRoutes(ctx) {
const FORMAT_NAMES = 'JPEG, PNG, GIF, WebP or AVIF';
const FORMAT_NAMES = 'JPEG, PNG, GIF, WebP or AVIF';
const sizeText = (b) => formatBytes(b).replace(/\.0 /, ' ');
const sizeText = (b) => formatBytes(b).replace(/\.0 /, ' ');
const homeFaq = () => [
const homeFaq = () => [
- { q: 'Is OpenVibe.Pics free?', a: `Yes, and there are no ads. A free OpenVibe account can upload ${cfg.imagesPerDay} pictures a day, each up to ${sizeText(cfg.maxImageBytes)}, and keep up to ${sizeText(cfg.quotaBytes)}.` },
+ { q: 'Is OpenVibe.Pics free?', a: `Yes. A free OpenVibe account can upload ${cfg.imagesPerDay} pictures a day, each up to ${sizeText(cfg.maxImageBytes)}, and keep up to ${sizeText(cfg.quotaBytes)}.` },
{ q: 'Who can see my pictures?', a: 'Only people you give the link to. Pictures are never listed, ranked or shown to search engines, and you can delete one at any time.' },
{ q: 'Who can see my pictures?', a: 'Only people you give the link to. Pictures are never listed, ranked or shown to search engines, and you can delete one at any time.' },
{ q: 'What is removed from my pictures?', a: 'Everything hidden in the file: the GPS position, the camera\'s serial number and the rest of its metadata. Every picture is decoded and saved again before anyone sees it.' },
{ q: 'What is removed from my pictures?', a: 'Everything hidden in the file: the GPS position, the camera\'s serial number and the rest of its metadata. Every picture is decoded and saved again before anyone sees it.' },
{ q: 'Which files can I upload?', a: `${FORMAT_NAMES}, animated GIF and WebP included. SVG and other formats are refused, so nothing is ever served as it was uploaded.` },
{ q: 'Which files can I upload?', a: `${FORMAT_NAMES}, animated GIF and WebP included. SVG and other formats are refused, so nothing is ever served as it was uploaded.` },
@@ -215,11 +215,11 @@ function createPicsRoutes(ctx) {
@@ -215,11 +215,11 @@ function createPicsRoutes(ctx) {
r.get('/', async (req, res) => {
r.get('/', async (req, res) => {
const mine = signedIn(req) ? (await store.imagesOf(s, whoOf(req).requester, { limit: 12 })).rows : [];
const mine = signedIn(req) ? (await store.imagesOf(s, whoOf(req).requester, { limit: 12 })).rows : [];
const canUpload = signedIn(req) || cfg.anonymous;
const canUpload = signedIn(req) || cfg.anonymous;
- const description = 'Upload a picture and get a link to share or embed anywhere. Albums, any size or format, no ads, with location and camera details removed.';
+ const description = 'Upload a picture and get a link to share or embed anywhere: albums, any size or format, and location and camera details removed from every picture.';
const faq = homeFaq();
const faq = homeFaq();
return page(req, res, {
return page(req, res, {
index: true,
index: true,
- title: `${SITE_NAME}: share pictures with a link, no ads`,
+ title: `${SITE_NAME}: share and embed pictures with a link`,
description,
description,
jsonLd: [
jsonLd: [
seo.jsonLd.website({ name: SITE_NAME, url: base, description }),
seo.jsonLd.website({ name: SITE_NAME, url: base, description }),
@@ -230,7 +230,7 @@ function createPicsRoutes(ctx) {
@@ -230,7 +230,7 @@ function createPicsRoutes(ctx) {
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>
-<p class="ovp-lede">Drop a picture, get a link to share or embed anywhere: on a forum, in a chat, on your site. No ads, no feed, and nothing about you hidden in the file.</p></header>
+<p class="ovp-lede">Drop a picture, get a link to share or embed anywhere: on a forum, in a chat, on your site. No feed to scroll, and nothing about you hidden in the file.</p></header>
${canUpload ? uploadForm(req) : html`<section class="ovp-drop ovp-drop--signin"><div class="ovp-drop-inner"><p class="ovp-drop-title">Sign in to upload</p><p class="ovp-drop-hint">Your OpenVibe account keeps your pictures and albums together, so you can delete them whenever you like.</p><p>${signInLink('/')}</p></div></section>`}
${canUpload ? uploadForm(req) : html`<section class="ovp-drop ovp-drop--signin"><div class="ovp-drop-inner"><p class="ovp-drop-title">Sign in to upload</p><p class="ovp-drop-hint">Your OpenVibe account keeps your pictures and albums together, so you can delete them whenever you like.</p><p>${signInLink('/')}</p></div></section>`}
${signedIn(req) ? html`<section class="ovp-section" aria-labelledby="h-latest"><div class="ovp-section-head"><h2 id="h-latest">Your latest pictures</h2><a href="/mine">All of them</a></div>${grid(mine, 'Nothing yet: your uploads show up here.')}</section>` : ''}
${signedIn(req) ? html`<section class="ovp-section" aria-labelledby="h-latest"><div class="ovp-section-head"><h2 id="h-latest">Your latest pictures</h2><a href="/mine">All of them</a></div>${grid(mine, 'Nothing yet: your uploads show up here.')}</section>` : ''}
<section class="ovp-section" aria-labelledby="h-how"><h2 id="h-how">How it works</h2><ol class="ovp-steps">
<section class="ovp-section" aria-labelledby="h-how"><h2 id="h-how">How it works</h2><ol class="ovp-steps">
@@ -579,7 +579,7 @@ ${row.state === 'hidden' ? html`<button class="ovp-btn" name="action" value="res
@@ -579,7 +579,7 @@ ${row.state === 'hidden' ? html`<button class="ovp-btn" name="action" value="res
.send(seo.sitemapXml(['/', '/updates', ...legal.PATHS].map((p) => ({ loc: `${base}${p}` })))));
.send(seo.sitemapXml(['/', '/updates', ...legal.PATHS].map((p) => ({ loc: `${base}${p}` })))));
r.get('/llms.txt', (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 })).send(seo.llmsTxt({
r.get('/llms.txt', (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 })).send(seo.llmsTxt({
name: SITE_NAME,
name: SITE_NAME,
- summary: 'Picture hosting on the OpenVibe network: upload a picture, get a link to share or embed, albums, no ads.',
+ summary: 'Picture hosting on the OpenVibe network: upload a picture, get a link to share or embed, albums, any size or format.',
details: 'Pictures are unlisted (reachable only by their link). Each one is re-encoded with its metadata removed, and can be had resized and converted at /i/<slug>/w/<320|640|1280|1920|2560>.<webp|avif|jpg|png>. Alt text: the uploader writes it or asks OpenVibe.AI for a draft (POST /api/v1/images/<slug>/describe). API: POST /api/v1/images with the picture as the body (an OpenVibe sign-in token as a Bearer).',
details: 'Pictures are unlisted (reachable only by their link). Each one is re-encoded with its metadata removed, and can be had resized and converted at /i/<slug>/w/<320|640|1280|1920|2560>.<webp|avif|jpg|png>. Alt text: the uploader writes it or asks OpenVibe.AI for a draft (POST /api/v1/images/<slug>/describe). API: POST /api/v1/images with the picture as the body (an OpenVibe sign-in token as a Bearer).',
sections: [{ title: 'Start here', links: [{ title: SITE_NAME, url: `${base}/` }, { title: 'Terms', url: `${base}/terms` }, { title: 'DMCA', url: `${base}/dmca` }] }],
sections: [{ title: 'Start here', links: [{ title: SITE_NAME, url: `${base}/` }, { title: 'Terms', url: `${base}/terms` }, { title: 'DMCA', url: `${base}/dmca` }] }],
})));
})));
@@ -780,7 +780,7 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar
@@ -780,7 +780,7 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar
}
}
const n = (v) => Number(v).toLocaleString('en-US');
const n = (v) => Number(v).toLocaleString('en-US');
const HOME_FAQ = (vu) => [
const HOME_FAQ = (vu) => [
- { q: 'Is OpenVibe.Video free?', a: `Yes. Watching needs no account, and uploading needs a free OpenVibe account. Each person can upload ${vu.perDay} videos a day and keep up to ${formatBytes(vu.quotaBytes)}. There are no ads.` },
+ { q: 'Is OpenVibe.Video free?', a: `Yes. Watching needs no account, and uploading needs a free OpenVibe account. Each person can upload ${vu.perDay} videos a day and keep up to ${formatBytes(vu.quotaBytes)}.` },
{ q: 'Which files can I upload?', a: `MP4 (MOV and M4V too), WebM and MKV, up to ${formatBytes(vu.maxBytes)} each. You can also record your camera or screen right in the browser.` },
{ q: 'Which files can I upload?', a: `MP4 (MOV and M4V too), WebM and MKV, up to ${formatBytes(vu.maxBytes)} each. You can also record your camera or screen right in the browser.` },
{ q: 'How do streams from OpenVibe.Live get here?', a: 'When a stream on OpenVibe.Live is recorded, the recording appears here after the stream ends, on the streamer\'s channel at openvibe.video/@name, together with the clips people cut from it.' },
{ q: 'How do streams from OpenVibe.Live get here?', a: 'When a stream on OpenVibe.Live is recorded, the recording appears here after the stream ends, on the streamer\'s channel at openvibe.video/@name, together with the clips people cut from it.' },
{ q: 'Where do the captions and chapters come from?', a: 'OpenVibe.AI turns the speech into captions, splits the video into chapters and writes a short summary. On your own uploads you can use the summary as the description.' },
{ q: 'Where do the captions and chapters come from?', a: 'OpenVibe.AI turns the speech into captions, splits the video into chapters and writes a short summary. On your own uploads you can use the summary as the description.' },
@@ -853,7 +853,7 @@ ${channels.length ? html`<section class="ovv-section" aria-labelledby="h-channel
@@ -853,7 +853,7 @@ ${channels.length ? html`<section class="ovv-section" aria-labelledby="h-channel
<section class="ovv-section" aria-labelledby="h-faq"><h2 id="h-faq">Questions</h2><div class="ovv-faq">${faq.map((f) => html`<details><summary>${f.q}</summary><p>${f.a}</p></details>`)}</div></section>
<section class="ovv-section" aria-labelledby="h-faq"><h2 id="h-faq">Questions</h2><div class="ovv-faq">${faq.map((f) => html`<details><summary>${f.q}</summary><p>${f.a}</p></details>`)}</div></section>
<section class="ovv-section" aria-labelledby="h-more"><h2 id="h-more">More on OpenVibe</h2><ul class="ovv-more" role="list">
<section class="ovv-section" aria-labelledby="h-more"><h2 id="h-more">More on OpenVibe</h2><ul class="ovv-more" role="list">
<li><a href="${livePublic}/"><strong>OpenVibe.Live</strong><span>Go live from your browser, no OBS needed.</span></a></li>
<li><a href="${livePublic}/"><strong>OpenVibe.Live</strong><span>Go live from your browser, no OBS needed.</span></a></li>
-<li><a href="${config.pics.url}/"><strong>OpenVibe.Pics</strong><span>Share pictures with a link, no ads.</span></a></li>
+<li><a href="${config.pics.url}/"><strong>OpenVibe.Pics</strong><span>Share and embed pictures with a link.</span></a></li>
<li><a href="${config.baseUrl}/"><strong>OpenVibe.Download</strong><span>Your files and backups, shared on your terms.</span></a></li>
<li><a href="${config.baseUrl}/"><strong>OpenVibe.Download</strong><span>Your files and backups, shared on your terms.</span></a></li>
<li><a href="https://openvibe.community/"><strong>OpenVibe.Community</strong><span>Forums and pastes for talking it over.</span></a></li>
<li><a href="https://openvibe.community/"><strong>OpenVibe.Community</strong><span>Forums and pastes for talking it over.</span></a></li>
</ul></section>`,
</ul></section>`,
@@ -79,7 +79,7 @@ function multipart(fields, file) {
@@ -79,7 +79,7 @@ function multipart(fields, file) {
await check('the home: how it works, the links, the API, the limits from config, questions that match its FAQPage', async () => {
await check('the home: how it works, the links, the API, the limits from config, questions that match its FAQPage', async () => {
const home = (await t.get('/', { headers: PICS })).text;
const home = (await t.get('/', { headers: PICS })).text;
assert.strictEqual((home.match(/<h1[\s>]/g) || []).length, 1, 'one h1');
assert.strictEqual((home.match(/<h1[\s>]/g) || []).length, 1, 'one h1');
- assert.match(home, /<title>OpenVibe\.Pics: share pictures with a link, no ads<\/title>/);
+ assert.match(home, /<title>OpenVibe\.Pics: share and embed pictures with a link<\/title>/);
const sections = ['How it works', 'The links a picture gets', 'Upload from your own app', 'What a free account gets', 'Questions', 'More on OpenVibe'];
const sections = ['How it works', 'The links a picture gets', 'Upload from your own app', 'What a free account gets', 'Questions', 'More on OpenVibe'];
assert.deepStrictEqual(sections.filter((h) => !home.includes(`>${h}</h2>`)), [], 'every section is there');
assert.deepStrictEqual(sections.filter((h) => !home.includes(`>${h}</h2>`)), [], 'every section is there');
assert.ok(home.includes('<td>20 MB</td>') && home.includes('<td>200</td>') && home.includes('<td>5 GB</td>'), 'the limits are the configured ones');
assert.ok(home.includes('<td>20 MB</td>') && home.includes('<td>200</td>') && home.includes('<td>5 GB</td>'), 'the limits are the configured ones');