openvibers/openvibe.live Commits

Live works like an app on a phone: Search is the navbar's magnifier (Live first), the duplicate Admin link goes, the bottom app bar comes on, the floating buttons stop covering the page, and the player's loader is the shared one over the stream's own picture (openvibe-shared 4.22.0) (#419)

OP OpenVibers committed 16 days ago · a19501c22b · 9 files, +83 −13

Changes

ddiff --git a/README.md b/README.md
iindex d9e4f589..cdef6229 100644
---- a/README.md
++++ b/README.md
@@@ -177,5 +177,5 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
<!-- versions:start -->
- openvibe-contracts: v0.222.0
- openvibe-sdk: v0.67.0
--- openvibe-shared: v4.19.0
++- openvibe-shared: v4.22.0
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 7e2cfe0d..25718bc9 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,7 +7,7 @@
"domain": "openvibe.live (www, ingest, whip)",
"contracts": "openvibe-contracts v0.222.0",
"sdk": "openvibe-sdk v0.67.0",
-- "shared": "openvibe-shared v4.19.0",
++ "shared": "openvibe-shared v4.22.0",
"capabilities": [
"live.chat_context.read",
"live.chat_effects.write",
ddiff --git a/package-lock.json b/package-lock.json
iindex fe4a8b3d..661fdb65 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -25,7 +25,7 @@
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.19.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.22.0",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sdp-transform": "^3.0.0",
@@@ -2688,9 +2688,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "4.19.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.19.0",
-- "integrity": "sha512-OksbPienuKJ88R5HrXYh3Dgh/TWZGwUL4FUQQmGhQe6wkQpbAxaknfAouU+HaI1xwbbGhQ5f9bVIKJv9+LrNQQ==",
++ "version": "4.22.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.22.0",
++ "integrity": "sha512-WZw3eE1zQzasWuNQPx7cCwkKTITZmgxQkP+NoyGGvqxPp9i/P9oRA9u34Pd/Gnhz4rYQCQ3BzV9IQfT+pNBMDQ==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex b176beab..252a997f 100644
---- a/package.json
++++ b/package.json
@@@ -44,7 +44,7 @@
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.67.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.19.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.22.0",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sdp-transform": "^3.0.0",
ddiff --git a/public/css/style.css b/public/css/style.css
iindex 8b7b6598..c3dab52a 100644
---- a/public/css/style.css
++++ b/public/css/style.css
@@@ -1837,6 +1837,16 @@ a.pulse-card:hover { border-color: var(--accent); transform: translateY(-2px); }
linear-gradient(180deg, rgba(5, 8, 14, 0.95), rgba(7, 11, 18, 0.86));
}
++/* The shared loader (Shared 4.21 loading.js, stream-player.js _renderSharedLoader) fills the player edge to edge with
++ the stream's own picture: the old card's padding, gradient and blurred ::before step aside. */
++.video-placeholder.ovld-host { padding: 0; background: #000; }
++.video-placeholder.ovld-host::before, .video-placeholder.ovld-host::after { display: none; }
++.video-placeholder.ovld-host > .ovld { position: absolute; inset: 0; z-index: 1; }
++/* An ended stream: the same overlay, still. */
++.ovld.is-still .ovld-ring::before, .ovld.is-still .ovld-ring::after { animation: none; }
++.ovld.is-still .ovld-ring::before { opacity: .3; }
++.ovld.is-still .ovld-line { display: none; }
++
/* ── Themed blurred-thumbnail loading screen ─────────────────── */
.video-placeholder.has-thumb::before {
inset: 0;
@@@ -9942,3 +9952,11 @@ body.ov-nav-overlay .openvibe-navbar.navbar.ovnav-top { background: transparent;
.openvibe-navbar.navbar .openvibe-navbar-avatar { width: 36px; height: 36px; border-radius: 50%; cursor: pointer; object-fit: cover; box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 50%, transparent); }
/* The site switcher stays beside the logo on a phone: the two balance chips fold into one there (Shared 4.16.0). */
@media (max-width: 560px) { .openvibe-navbar.navbar .ovnav-chip { padding: 0 8px; } }
++
++/* Phones with the app bar (Shared 4.20, js/ov-navbar-live.js): Chat is a tab, so the global chat button steps aside; the
++ messages button sits above the bar and the page keeps room for it (it covered a channel's followers and buttons). */
++@media (max-width: 760px) {
++ html.ov-has-appbar #floating-chat-fab { display: none !important; }
++ html.ov-has-appbar .messenger-toggle { bottom: calc(var(--ovnav-appbar-h, 58px) + 14px); }
++ html.ov-has-appbar body { padding-bottom: calc(var(--ovnav-appbar-h, 58px) + 64px); }
++}
ddiff --git a/public/features.json b/public/features.json
iindex 9be58311..2a7015ce 100644
---- a/public/features.json
++++ b/public/features.json
@@@ -53,6 +53,7 @@
"clipJobs"
],
"js": [
++ "/shared/loading.js",
"/js/player-shape.js",
"/js/stream-player.js",
"/js/stream-pip.js"
ddiff --git a/public/js/ov-navbar-live.js b/public/js/ov-navbar-live.js
iindex 0f35d95d..475721c2 100644
---- a/public/js/ov-navbar-live.js
++++ b/public/js/ov-navbar-live.js
@@@ -16,20 +16,32 @@
const nav = () => window.OpenVibeNavbar;
let booted = false;
++ // The bar's places, most used first (the owner, 2026-10-28: "move search after the Rundown and the drop-down… reanalyze
++ // the nav bars… redundant options"). Search is the navbar's magnifier now (Shared 4.21: Live's results first, then all
++ // of OpenVibe; config.search below); Admin is in the account menu (Admin Panel) and was here twice for staff.
function links(user) {
-- const staff = !!(user && ((user.capabilities && user.capabilities.admin_panel) || (typeof hasCapability === 'function' && hasCapability('can_access_staff_console'))));
return [
{ id: 'home', page: 'home', label: 'Home', href: '/', icon: 'fa-house' },
// VODs, clips and pastes people made are one feed; what the AI made is the other.
{ id: 'content', page: 'content', label: 'Content', href: '/content', icon: 'fa-photo-film' },
{ id: 'moments', page: 'moments', label: 'Moments', href: '/moments', icon: 'fa-wand-magic-sparkles' },
-- { id: 'search', page: 'search', label: 'Search', href: '/search', icon: 'fa-magnifying-glass' },
{ id: 'chat', page: 'chat', label: 'Chat', href: '/chat', icon: 'fa-comments' },
{ id: 'rundown', page: 'rundown', label: 'Rundown', href: '/rundown', icon: 'fa-newspaper' },
{ id: 'broadcast', page: 'broadcast', elId: 'nav-broadcast', label: 'Go Live', href: '/broadcast', icon: 'fa-tower-broadcast', dot: false, dotId: 'nav-live-dot',
onClick: (e) => call('goLiveNav', e),
children: user ? [{ id: 'dashboard', label: 'Dashboard', href: '/dashboard', icon: 'fa-gauge-high', onClick: (e) => call('dashNav', e) }] : [] },
-- { id: 'admin', page: 'admin', label: 'Admin', href: 'https://openvibe.network/admin', icon: 'fa-shield-halved', hidden: !staff },
++ ];
++ }
++
++ // Phones: the bottom tab bar (Shared 4.20's appBar). Home, what people made, going live in the middle, chat, and the
++ // Rundown; everything else is in More (the drawer).
++ function appBar() {
++ return [
++ { id: 'home', page: 'home', label: 'Home', href: '/', icon: 'fa-house' },
++ { id: 'content', page: 'content', label: 'Videos', href: '/content', icon: 'fa-photo-film' },
++ { id: 'broadcast', page: 'broadcast', label: 'Go Live', href: '/broadcast', icon: 'fa-tower-broadcast', onClick: (e) => call('goLiveNav', e) },
++ { id: 'chat', page: 'chat', label: 'Chat', href: '/chat', icon: 'fa-comments' },
++ { id: 'rundown', page: 'rundown', label: 'Rundown', href: '/rundown', icon: 'fa-newspaper' },
];
}
@@@ -62,7 +74,9 @@
// this sign-in (Shared 4.13.0), which returns to the page they were on.
loginUrl: '/api/auth/sso/login?next={path}', networkLinks: 0, history: false, silentLogin: false, fedcm: false, recent: false,
notificationsRealtime: true, // the bell hears new notifications over OpenVibe.Events (Shared 1.22.0)
-- links: links(user), menu: menu(user),
++ links: links(user), menu: menu(user), appBar: appBar(), adminLink: false, // Admin Panel is in the account menu
++ // The magnifier (Shared 4.21): Live's streams, recordings and clips first, Live's own search page, then OpenVibe.
++ search: { owner: 'live', label: 'OpenVibe.Live', page: '/search' },
chips: [
// On a phone the two fold into one chip (Shared 4.16.0); its panel lists them by label.
{ id: 'coins', icon: 'fa-coins', tone: 'gold', value: '0', valueId: 'nav-coins-amount', label: 'OpenCoins', title: 'OpenCoins: click to open your inventory', onClick: () => call('openCosmeticsModal') },
ddiff --git a/public/js/stream-player.js b/public/js/stream-player.js
iindex 7286987e..307d87c1 100644
---- a/public/js/stream-player.js
++++ b/public/js/stream-player.js
@@@ -471,6 +471,35 @@ function _ensurePlayerLoaderStructure(placeholder) {
return placeholder._playerLoaderEls;
}
++// The network's one loading look (Shared 4.21 loading.js; the owner, 2026-10-28, of the old card: "old and outdated…
++// consider the latest frames of the stream… no borders and wasted space and padding… minimalistic… a better animation").
++// It covers the player edge to edge: the stream's own thumbnail blurred behind, the phase in a word with one line under
++// it, a thin line that fills step by step; help (Try again, Copy details) only when it is slow or fails. Live's phases,
++// timers and diagnostics are unchanged; without loading.js the card above is still drawn.
++function _renderSharedLoader(placeholder, state, progress, briefCopy, showExpanded) {
++ let l = placeholder._ovLoader;
++ if (!l || !placeholder.contains(l.element)) {
++ placeholder.textContent = '';
++ placeholder._playerLoaderEls = null;
++ placeholder.classList.add('ovld-host');
++ l = placeholder._ovLoader = window.OpenVibeLoading.mount(placeholder, { poster: placeholder.dataset.thumb || '', steps: progress.totalSteps });
++ }
++ const actions = [
++ { label: 'Try again', onClick: () => reloadPlayerPage() },
++ { label: 'Copy details', onClick: () => copyPlayerDiagnostics() },
++ ];
++ if (state.severity === 'error') {
++ l.fail({ title: state.title || 'Playback stopped', detail: state.detail || briefCopy, actions });
++ return;
++ }
++ // An ended stream is not loading: the same picture and words, still (no ring turning, no line filling).
++ const ended = state.phase === 'ended';
++ l.element.classList.toggle('is-still', ended);
++ l.update({ label: ended ? (state.title || 'Stream has ended') : _getPlayerPhaseDisplayLabel(state.phase), sub: ended ? (state.detail || briefCopy) : briefCopy,
++ ...(ended ? {} : { step: progress.currentIndex, steps: progress.totalSteps }) });
++ if (showExpanded && !ended) l.slow({ hint: state.hint || state.detail || 'This is taking longer than usual.', actions });
++}
++
function _animatePlayerLoaderStatus(shell) {
if (!shell) return;
shell.classList.remove('is-phase-changing');
@@@ -511,6 +540,10 @@ function _renderPlayerPlaceholder() {
placeholder.dataset.phase = state.phase || 'signal';
placeholder.dataset.severity = state.severity || 'info';
++ if (window.OpenVibeLoading) {
++ _renderSharedLoader(placeholder, state, progress, briefCopy, showExpanded);
++ return;
++ }
const els = _ensurePlayerLoaderStructure(placeholder);
if (placeholder.dataset.renderSignature !== renderSignature) {
_animatePlayerLoaderStatus(els.shell);
@@@ -861,10 +894,13 @@ function _applyLoaderThumb(stream) {
const thumb = stream && stream.thumbnail_url;
// Reset any previous stream's theming.
placeholder.classList.remove('has-thumb');
-- ['--loader-thumb', '--loader-a', '--loader-c'].forEach(v => placeholder.style.removeProperty(v));
++ ['--loader-thumb', '--loader-a', '--loader-c', '--accent-light'].forEach(v => placeholder.style.removeProperty(v));
++ delete placeholder.dataset.thumb;
if (!thumb) return;
placeholder.style.setProperty('--loader-thumb', `url("${thumb}")`);
placeholder.classList.add('has-thumb');
++ placeholder.dataset.thumb = thumb; // the shared loader's picture (loading.js), now or when it mounts
++ if (placeholder._ovLoader && placeholder.contains(placeholder._ovLoader.element)) placeholder._ovLoader.update({ poster: thumb });
try {
const img = new Image();
img.crossOrigin = 'anonymous';
@@@ -904,6 +940,7 @@ function _applyLoaderThumb(stream) {
const c = cols[1] || cols[0];
const mid = { r: (a.r + c.r) / 2, g: (a.g + c.g) / 2, b: (a.b + c.b) / 2 };
placeholder.style.setProperty('--loader-a', rgb(a));
++ placeholder.style.setProperty('--accent-light', rgb(a)); // the shared loader's ring and line take the stream's colour
placeholder.style.setProperty('--loader-b', rgb(mid));
placeholder.style.setProperty('--loader-c', rgb(c));
}
ddiff --git a/scripts/perf/check-budgets.js b/scripts/perf/check-budgets.js
iindex c4545f4a..ac4550cb 100644
---- a/scripts/perf/check-budgets.js
++++ b/scripts/perf/check-budgets.js
@@@ -23,7 +23,7 @@ const BUDGETS = {
htmlBrotliKB: 30,
jsFiles: 26, // measured 23 (incl. route scripts for "/")
jsRawKB: 1150, // measured ~1000
-- jsBrotliKB: 270, // measured 269.6 (2026-10-28: openvibe-shared 4.19.0, the account menu's gear and people-group icons inline; 268 on 2026-10-27: openvibe-shared 4.16.0, the folded balance chip, Across OpenVibe with site marks and the icon preload in the navbar); 265.3 (2026-10-27: openvibe-shared 4.15.0, the compact footer opens into the whole one, and the pulse line); 263.3 on 10-23 (openvibe-shared 4.9.0, status text twins in every theme); 262.5 on 10-17 (openvibe-shared 4.4.0, every site in the launcher)
++ jsBrotliKB: 272, // measured 270.5 (2026-10-28: openvibe-shared 4.22.0, the navbar's search magnifier with / and Ctrl+K and the More menu; search.js and loading.js load only when used); 269.6 (2026-10-28: openvibe-shared 4.19.0, the account menu's gear and people-group icons inline; 268 on 2026-10-27: openvibe-shared 4.16.0, the folded balance chip, Across OpenVibe with site marks and the icon preload in the navbar); 265.3 (2026-10-27: openvibe-shared 4.15.0, the compact footer opens into the whole one, and the pulse line); 263.3 on 10-23 (openvibe-shared 4.9.0, status text twins in every theme); 262.5 on 10-17 (openvibe-shared 4.4.0, every site in the launcher)
cssRawKB: 270, // measured ~243 since the first-paint subset (style.core.css) replaced style.css (~717 before)
cssBrotliKB: 55, // measured ~47.5 (was ~130)
};