openvibers/openvibe.mediahub Commits
Video home: a recording under 15 seconds gets no tile on the front page (#84)
Changes
@@@ -34,6 +34,7 @@ const { ACCESS_COOKIE, REFRESH_COOKIE } = require('../auth/sso');const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');const HOME_LIMIT = 10;++const MIN_HOME_SECONDS = 15;const SITEMAP_LIMIT = 30; // Live's feed page size (its MAX_LIMIT)const SITEMAP_PAGES = 5; // per kind: up to 150 recordings and 150 clipsconst CHANNEL_CLIPS_LIMIT = 12;@@@ -666,10 +667,13 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar// ── Home ────────────────────────────────────────────────r.get('/', async (req, res) => {const [vods, clips] = await Promise.all([-- live.feed({ type: 'vods', limit: HOME_LIMIT }),++ live.feed({ type: 'vods', limit: HOME_LIMIT + 6 }),live.feed({ type: 'clips', limit: HOME_LIMIT }),]);-- const vodItems = (vods && Array.isArray(vods.items)) ? vods.items : [];++ // A stream that ended seconds after it started (a test, a dropped connection) is not worth a tile on the front++ // page; it stays on its channel.++ const vodItems = ((vods && Array.isArray(vods.items)) ? vods.items : [])++ .filter((x) => !(Number(x.duration_seconds) > 0 && Number(x.duration_seconds) < MIN_HOME_SECONDS)).slice(0, HOME_LIMIT);const clipItems = (clips && Array.isArray(clips.items)) ? clips.items : [];const uploaded = await ctx.videoUploads.latest(HOME_LIMIT);return page(req, res, {@@@ -104,7 +104,7 @@ function startLive() {const p = url.pathname;if (p === '/api/content/feed') {const type = url.searchParams.get('type');-- if (type === 'vods') return json(200, { items: [vodFeedItem], next: null });++ if (type === 'vods') return json(200, { items: [vodFeedItem, { ...vodFeedItem, key: 'vod:109', id: 109, title: 'A two-second blip', duration_seconds: 2 }], next: null });if (type === 'clips') return json(200, { items: [clipFeedItem], next: null });return json(200, { items: [], next: null });}@@@ -237,6 +237,7 @@ function countOccurrences(haystack, needle) {assert.ok(r.text.includes('/v/101'));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');// openvibe.media thumbnails are asked for in sizes: a 480 px WebP by default, 320/480/640 in the srcset.assert.ok(r.text.includes('src="https://openvibe.media/t/101.jpg?w=480&fmt=webp"'), 'a WebP thumbnail near the card width');assert.ok(r.text.includes('https://openvibe.media/t/101.jpg?w=320&fmt=webp 320w, https://openvibe.media/t/101.jpg?w=480&fmt=webp 480w, https://openvibe.media/t/101.jpg?w=640&fmt=webp 640w'), 'the srcset ladder');
@@ -34,6 +34,7 @@ const { ACCESS_COOKIE, REFRESH_COOKIE } = require('../auth/sso');
@@ -34,6 +34,7 @@ const { ACCESS_COOKIE, REFRESH_COOKIE } = require('../auth/sso');
const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
const HOME_LIMIT = 10;
const HOME_LIMIT = 10;
+const MIN_HOME_SECONDS = 15;
const SITEMAP_LIMIT = 30; // Live's feed page size (its MAX_LIMIT)
const SITEMAP_LIMIT = 30; // Live's feed page size (its MAX_LIMIT)
const SITEMAP_PAGES = 5; // per kind: up to 150 recordings and 150 clips
const SITEMAP_PAGES = 5; // per kind: up to 150 recordings and 150 clips
const CHANNEL_CLIPS_LIMIT = 12;
const CHANNEL_CLIPS_LIMIT = 12;
@@ -666,10 +667,13 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar
@@ -666,10 +667,13 @@ ${kind === 'vod' ? html`<label class="ovv-field"><span>Description</span><textar
// ── Home ────────────────────────────────────────────────
// ── Home ────────────────────────────────────────────────
r.get('/', async (req, res) => {
r.get('/', async (req, res) => {
const [vods, clips] = await Promise.all([
const [vods, clips] = await Promise.all([
- live.feed({ type: 'vods', limit: HOME_LIMIT }),
+ live.feed({ type: 'vods', limit: HOME_LIMIT + 6 }),
live.feed({ type: 'clips', limit: HOME_LIMIT }),
live.feed({ type: 'clips', limit: HOME_LIMIT }),
]);
]);
- const vodItems = (vods && Array.isArray(vods.items)) ? vods.items : [];
+ // A stream that ended seconds after it started (a test, a dropped connection) is not worth a tile on the front
+ // page; it stays on its channel.
+ const vodItems = ((vods && Array.isArray(vods.items)) ? vods.items : [])
+ .filter((x) => !(Number(x.duration_seconds) > 0 && Number(x.duration_seconds) < MIN_HOME_SECONDS)).slice(0, HOME_LIMIT);
const clipItems = (clips && Array.isArray(clips.items)) ? clips.items : [];
const clipItems = (clips && Array.isArray(clips.items)) ? clips.items : [];
const uploaded = await ctx.videoUploads.latest(HOME_LIMIT);
const uploaded = await ctx.videoUploads.latest(HOME_LIMIT);
return page(req, res, {
return page(req, res, {
@@ -104,7 +104,7 @@ function startLive() {
@@ -104,7 +104,7 @@ function startLive() {
const p = url.pathname;
const p = url.pathname;
if (p === '/api/content/feed') {
if (p === '/api/content/feed') {
const type = url.searchParams.get('type');
const type = url.searchParams.get('type');
- if (type === 'vods') return json(200, { items: [vodFeedItem], next: null });
+ if (type === 'vods') return json(200, { items: [vodFeedItem, { ...vodFeedItem, key: 'vod:109', id: 109, title: 'A two-second blip', duration_seconds: 2 }], next: null });
if (type === 'clips') return json(200, { items: [clipFeedItem], next: null });
if (type === 'clips') return json(200, { items: [clipFeedItem], next: null });
return json(200, { items: [], next: null });
return json(200, { items: [], next: null });
}
}
@@ -237,6 +237,7 @@ function countOccurrences(haystack, needle) {
@@ -237,6 +237,7 @@ function countOccurrences(haystack, needle) {
assert.ok(r.text.includes('/v/101'));
assert.ok(r.text.includes('/v/101'));
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');
// openvibe.media thumbnails are asked for in sizes: a 480 px WebP by default, 320/480/640 in the srcset.
// openvibe.media thumbnails are asked for in sizes: a 480 px WebP by default, 320/480/640 in the srcset.
assert.ok(r.text.includes('src="https://openvibe.media/t/101.jpg?w=480&fmt=webp"'), 'a WebP thumbnail near the card width');
assert.ok(r.text.includes('src="https://openvibe.media/t/101.jpg?w=480&fmt=webp"'), 'a WebP thumbnail near the card width');
assert.ok(r.text.includes('https://openvibe.media/t/101.jpg?w=320&fmt=webp 320w, https://openvibe.media/t/101.jpg?w=480&fmt=webp 480w, https://openvibe.media/t/101.jpg?w=640&fmt=webp 640w'), 'the srcset ladder');
assert.ok(r.text.includes('https://openvibe.media/t/101.jpg?w=320&fmt=webp 320w, https://openvibe.media/t/101.jpg?w=480&fmt=webp 480w, https://openvibe.media/t/101.jpg?w=640&fmt=webp 640w'), 'the srcset ladder');