69 lines
2.7 KiB
JavaScript
69 lines
2.7 KiB
JavaScript
import { PdfViewer } from './pdf-viewer.js';
|
|
|
|
const $ = (s) => document.querySelector(s);
|
|
const parts = location.pathname.split('/').filter(Boolean);
|
|
const sessionId = parts[parts.length - 1];
|
|
const params = new URLSearchParams(location.search);
|
|
const token = params.get('token') || ''; // presenter token: mirror the console even after code rotation
|
|
const accessCode = params.get('k') || ''; // fallback for asset access
|
|
|
|
const socket = io();
|
|
let kind = null, built = false, viewer = null, videoEl = null;
|
|
|
|
function msg(text) { $('#stage').innerHTML = `<div class="display-msg">${text}</div>`; }
|
|
|
|
// Join as presenter (display-only — it never sends control) so it stays in sync
|
|
// with the console and isn't counted as an audience viewer.
|
|
socket.on('connect', () => socket.emit('join', { sessionId, role: 'presenter', token, k: accessCode }));
|
|
socket.on('error:join', (m) => msg(m));
|
|
socket.on('ended', () => msg('The presentation has ended.'));
|
|
socket.on('state', onState);
|
|
|
|
async function onState(s) {
|
|
if (!s.meta) return;
|
|
document.title = (s.meta.title || 'Presentation') + ' — MPS';
|
|
if (s.meta.status === 'processing') { msg('Preparing…'); return; }
|
|
if (s.meta.status === 'error') { msg('This presentation could not be prepared.'); return; }
|
|
if (!built) { await build(s.meta); built = true; }
|
|
if (kind === 'pdf') viewer.render(s.currentPage || 1);
|
|
else if (kind === 'video') applyVideo(s.video);
|
|
}
|
|
|
|
async function build(meta) {
|
|
kind = meta.kind;
|
|
const stage = $('#stage');
|
|
stage.innerHTML = '';
|
|
const src = `/s/${meta.id}/${meta.viewerFile}${accessCode ? `?k=${encodeURIComponent(accessCode)}` : ''}`;
|
|
if (kind === 'pdf') {
|
|
viewer = new PdfViewer(stage);
|
|
await viewer.load(src);
|
|
} else if (kind === 'video') {
|
|
videoEl = document.createElement('video');
|
|
videoEl.src = src;
|
|
videoEl.playsInline = true;
|
|
videoEl.preload = 'auto';
|
|
videoEl.setAttribute('webkit-playsinline', '');
|
|
stage.appendChild(videoEl);
|
|
} else if (kind === 'image') {
|
|
const img = document.createElement('img');
|
|
img.src = src; img.alt = meta.title || '';
|
|
stage.appendChild(img);
|
|
}
|
|
}
|
|
|
|
function applyVideo(v) {
|
|
if (!videoEl || !v) return;
|
|
const rate = v.rate || 1;
|
|
if (v.playing) {
|
|
const elapsed = (Date.now() - (v.updatedAt || Date.now())) / 1000;
|
|
const expected = (v.time || 0) + elapsed * rate;
|
|
if (Math.abs(videoEl.currentTime - expected) > 0.8) videoEl.currentTime = expected;
|
|
videoEl.playbackRate = rate;
|
|
// Play with sound where allowed; fall back to muted so the image still tracks.
|
|
videoEl.play().catch(() => { videoEl.muted = true; videoEl.play().catch(() => {}); });
|
|
} else {
|
|
videoEl.pause();
|
|
if (typeof v.time === 'number' && Math.abs(videoEl.currentTime - v.time) > 0.4) videoEl.currentTime = v.time;
|
|
}
|
|
}
|