aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-05 01:16:12 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-05 01:16:12 +0200
commit243bcf017b4957237b3ddf556bd0a37296eb8798 (patch)
tree7d0820796737aad4dcfb89de7bec1e7271759af6 /packages/meshbay-hub/src/meshbay_hub/static/video-player.js
parentac8ed437659d765120d1b0cc9f83e942549b479f (diff)
downloadmeshbay-243bcf017b4957237b3ddf556bd0a37296eb8798.tar.gz
feat(cast): photos on the TV, and a television chosen once for the session
A cast button in Videos' toolbar, at the top of Photos, in an album's bar and in the lightbox, in a group and in Search alike. A television chosen there is kept for the session: a film opened plays on it with the player as its remote from the start, and a photo opened in the lightbox is shown on it, scaled to 1920x1080, upright, as JPEG. The lightbox gains a slideshow. The relay serves one photo at /image behind the stream's token, on the desktop and on Android; the shell, not the page, decides that the receiver loads it as a picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/video-player.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/video-player.js20
1 files changed, 15 insertions, 5 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
index 2c424fb..ed1f18c 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
@@ -6,6 +6,7 @@ import { Icon } from './icon.js';
import { formatSize, CHUNK_SIZE, pipelinedDownload } from './file-utils.js';
import { loadAuth } from './hub-client.js';
import * as platform from './platform.js';
+import { castSession } from './cast-session.js';
// Seconds of already-watched video kept in the SourceBuffer, and the queue depth
// past which we start making room before being forced to.
@@ -484,17 +485,22 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) {
// Where the stream the node is sending begins, in film time. The same number
// the SourceBuffer gets as its `timestampOffset`.
const streamStartRef = useRef(0);
- const [castActive, setCastActive] = useState(false);
+ // A television already chosen this session (cast-session.js) takes the film
+ // from its first segment: the cast starts as a restart would, pending until
+ // the init segment arrives, and the player opens as its remote. Read once —
+ // a television chosen while this film plays is chosen here, by the picker.
+ const [autoCast] = useState(() => (castSession.supported ? castSession.device : null));
+ const [castActive, setCastActive] = useState(Boolean(autoCast));
const [castUrl, setCastUrl] = useState(null);
const [castPickerOpen, setCastPickerOpen] = useState(false);
const [castDevices, setCastDevices] = useState([]);
const [castScanning, setCastScanning] = useState(false);
- const [castDeviceName, setCastDeviceName] = useState(null);
- const castActiveRef = useRef(false);
+ const [castDeviceName, setCastDeviceName] = useState(autoCast ? autoCast.name : null);
+ const castActiveRef = useRef(Boolean(autoCast));
const castCodecRef = useRef(null);
const initSegmentRef = useRef(null);
- const castRestartPendingRef = useRef(false);
- const castDeviceRef = useRef(null);
+ const castRestartPendingRef = useRef(Boolean(autoCast));
+ const castDeviceRef = useRef(autoCast);
const castRestartGenRef = useRef(0);
// The receiver as last polled: { playerState, position } with the position
// on the film's timeline, or null before it has answered.
@@ -1758,7 +1764,10 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) {
return () => { stopped = true; clearInterval(id); };
}, [remoteMode]);
+ // Stopping here is stopping for the session: the next film or photo stays
+ // on this device until a television is chosen again.
const stopCast = async () => {
+ castSession.forget();
await platform.cast.chromecastDisconnect().catch(() => {});
await platform.cast.stop();
setCastActive(false); castActiveRef.current = false;
@@ -1896,6 +1905,7 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) {
${castDevices.map(d => html`
<button class="cast-picker-item" onClick=${() => {
setCastPickerOpen(false);
+ castSession.choose(d);
setCastDeviceName(d.name);
setCastActive(true); castActiveRef.current = true;
castRestartPendingRef.current = true;