248 lines
8.7 KiB
JavaScript
248 lines
8.7 KiB
JavaScript
let ws;
|
|
const videoPlayer = document.getElementById('videoPlayer');
|
|
const videoOverlay = document.getElementById('videoOverlay');
|
|
const progressContainer = document.getElementById('progressContainer');
|
|
const progressFill = document.getElementById('progressFill');
|
|
const timer = document.getElementById('timer');
|
|
const loadingSpinner = document.getElementById('loadingSpinner');
|
|
const loadingStatusText = document.getElementById('loadingStatusText'); // new element
|
|
let backendDuration = null; // Duration provided by backend
|
|
|
|
console.log('Renderer loaded. DOM elements initialized.');
|
|
|
|
// Console log passthrough to Electron main
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require('electron');
|
|
['log', 'error', 'warn', 'info'].forEach(level => {
|
|
const orig = console[level];
|
|
console[level] = function(...args) {
|
|
ipcRenderer.send('ui-log', { level, args });
|
|
orig.apply(console, args);
|
|
};
|
|
});
|
|
console.log('Electron ipcRenderer logging enabled.');
|
|
}
|
|
|
|
// Always muted for security
|
|
videoPlayer.muted = true;
|
|
console.log('Video player muted.');
|
|
|
|
function connectWebSocket() {
|
|
console.log('Connecting to WebSocket...');
|
|
ws = new WebSocket('ws://localhost:8001');
|
|
ws.onmessage = (event) => {
|
|
console.log('WebSocket message received:', event.data);
|
|
try {
|
|
const message = JSON.parse(event.data);
|
|
console.log('Parsed message:', message);
|
|
if (message.type === 'play_video') {
|
|
console.log('play_video command received:', message);
|
|
playVideo(
|
|
message.url,
|
|
message.location || "center",
|
|
message.size || 5,
|
|
message.duration ?? null
|
|
);
|
|
} else if (message.type === 'stop_video') {
|
|
console.log('stop_video command received.');
|
|
stopVideo();
|
|
} else if (message.type === 'status_update') {
|
|
console.log('status_update received:', message.status);
|
|
updateLoadingStatus(message.status);
|
|
}
|
|
} catch (error) {
|
|
showError('Error parsing message from server.');
|
|
console.error('Error parsing message:', error);
|
|
}
|
|
};
|
|
ws.onclose = () => {
|
|
console.warn('WebSocket closed. Retrying...');
|
|
showError('Lost connection to WebSocket server. Retrying...');
|
|
setTimeout(connectWebSocket, 3000);
|
|
};
|
|
ws.onerror = (err) => {
|
|
console.error('WebSocket error:', err);
|
|
showError('WebSocket connection error. Is the server running?');
|
|
};
|
|
}
|
|
|
|
function updateLoadingStatus(status) {
|
|
console.log('Updating loading status:', status);
|
|
if (loadingStatusText) {
|
|
loadingStatusText.textContent = status;
|
|
loadingSpinner.style.display = 'flex';
|
|
videoPlayer.style.display = 'none';
|
|
progressContainer.style.display = 'none';
|
|
videoOverlay.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
function playVideo(url, location = "center", size = 5, duration = null) {
|
|
console.log('[UI] playVideo called:', { url, location, size, duration });
|
|
backendDuration = typeof duration === 'number' && duration > 0 ? duration : null;
|
|
|
|
videoPlayer.src = url;
|
|
videoOverlay.classList.remove('hidden');
|
|
setOverlayPosition(location, size);
|
|
|
|
// Show loading spinner, hide video and progress until ready
|
|
loadingSpinner.style.display = 'flex';
|
|
videoPlayer.style.display = 'none';
|
|
progressContainer.style.display = backendDuration ? 'block' : 'none';
|
|
|
|
// Start loading and wait for canplay to avoid black frames
|
|
videoPlayer.load();
|
|
console.log('[UI] Video loading started.');
|
|
|
|
const cleanup = () => {
|
|
videoPlayer.removeEventListener('canplay', onCanPlay);
|
|
videoPlayer.removeEventListener('error', onError);
|
|
};
|
|
|
|
const onCanPlay = () => {
|
|
console.log('[UI] canplay: starting playback');
|
|
loadingSpinner.style.display = 'none';
|
|
videoPlayer.style.display = '';
|
|
videoPlayer.play().then(() => {
|
|
console.log('[UI] Video playback started.');
|
|
}).catch((err) => {
|
|
console.warn('[UI] Video playback failed:', err);
|
|
});
|
|
updateProgress();
|
|
cleanup();
|
|
};
|
|
|
|
const onError = (e) => {
|
|
console.error('[UI] Video error:', e);
|
|
showError(e.message ? e.message.toString() : 'Unknown video error');
|
|
cleanup();
|
|
};
|
|
|
|
videoPlayer.addEventListener('canplay', onCanPlay, { once: true });
|
|
videoPlayer.addEventListener('error', onError, { once: true });
|
|
|
|
// Safety fallback in case canplay doesn't fire quickly
|
|
setTimeout(() => {
|
|
if (loadingSpinner.style.display !== 'none') {
|
|
console.warn('[UI] Fallback: forcing playback after timeout.');
|
|
onCanPlay();
|
|
}
|
|
}, 2000);
|
|
}
|
|
|
|
function stopVideo() {
|
|
console.log('stopVideo called.');
|
|
hideVideo();
|
|
}
|
|
|
|
function sendStoppedMessage() {
|
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
ws.send(JSON.stringify({ type: "stopped" }));
|
|
console.log('Sent "stopped" message to backend.');
|
|
}
|
|
}
|
|
|
|
function hideVideo() {
|
|
console.log('hideVideo called.');
|
|
backendDuration = null; // Clear backend-provided duration
|
|
videoOverlay.classList.add('hidden');
|
|
videoPlayer.pause();
|
|
videoPlayer.src = '';
|
|
videoPlayer.style.width = '100vw';
|
|
videoPlayer.style.height = '100vh';
|
|
progressContainer.style.display = 'none';
|
|
progressFill.style.width = '0%';
|
|
timer.textContent = '';
|
|
loadingSpinner.style.display = 'none';
|
|
videoPlayer.style.display = '';
|
|
sendStoppedMessage(); // Notify backend when video stops
|
|
}
|
|
|
|
// Start WebSocket connection when page loads
|
|
console.log('Starting WebSocket connection...');
|
|
connectWebSocket();
|
|
|
|
videoPlayer.addEventListener('ended', () => {
|
|
console.log('Video ended event.');
|
|
hideVideo();
|
|
sendStoppedMessage(); // Notify backend when video ends
|
|
});
|
|
videoPlayer.addEventListener('timeupdate', () => {
|
|
// This can be noisy, so only log occasionally
|
|
if (Math.floor(videoPlayer.currentTime) % 5 === 0) {
|
|
console.log('Video timeupdate:', videoPlayer.currentTime);
|
|
}
|
|
updateProgress();
|
|
});
|
|
|
|
function updateProgress() {
|
|
let current = 0;
|
|
try {
|
|
current = Number(videoPlayer.currentTime) || 0;
|
|
if (typeof backendDuration === 'number' && backendDuration > 0) {
|
|
const total = backendDuration;
|
|
const percent = Math.min((current / total) * 100, 100);
|
|
progressFill.style.width = percent + '%';
|
|
timer.textContent = formatTime(current) + ' / ' + formatTime(total);
|
|
// Optional: reduce noisy logs
|
|
// console.log(`[UI] Progress: ${percent.toFixed(2)}%`);
|
|
} else {
|
|
// No duration provided; hide progress UI
|
|
progressFill.style.width = '0%';
|
|
timer.textContent = formatTime(current) + ' / --:--';
|
|
progressContainer.style.display = 'none';
|
|
}
|
|
} catch (err) {
|
|
progressFill.style.width = '0%';
|
|
timer.textContent = formatTime(current) + ' / --:--';
|
|
console.error('[UI] Error updating progress:', err);
|
|
}
|
|
}
|
|
|
|
function formatTime(seconds) {
|
|
seconds = Math.floor(seconds);
|
|
const m = Math.floor(seconds / 60);
|
|
const s = seconds % 60;
|
|
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
}
|
|
|
|
function setOverlayPosition(location, size) {
|
|
console.log('setOverlayPosition called:', { location, size });
|
|
// Reset styles
|
|
videoOverlay.style.justifyContent = '';
|
|
videoOverlay.style.alignItems = '';
|
|
videoOverlay.style.top = '';
|
|
videoOverlay.style.left = '';
|
|
videoOverlay.style.right = '';
|
|
videoOverlay.style.bottom = '';
|
|
videoOverlay.style.width = '100vw';
|
|
videoOverlay.style.height = '100vh';
|
|
|
|
// Calculate video size as percentage of viewport (10% to 100%)
|
|
const percent = Math.min(Math.max(size, 1), 10) * 10;
|
|
videoPlayer.style.width = percent + 'vw';
|
|
videoPlayer.style.height = percent + 'vh';
|
|
|
|
// Position overlay and video
|
|
switch (location) {
|
|
case 'top_left':
|
|
videoOverlay.style.justifyContent = 'flex-start';
|
|
videoOverlay.style.alignItems = 'flex-start';
|
|
break;
|
|
case 'bottom_left':
|
|
videoOverlay.style.justifyContent = 'flex-start';
|
|
videoOverlay.style.alignItems = 'flex-end';
|
|
break;
|
|
case 'top_right':
|
|
videoOverlay.style.justifyContent = 'flex-end';
|
|
videoOverlay.style.alignItems = 'flex-start';
|
|
break;
|
|
case 'center':
|
|
default:
|
|
videoOverlay.style.justifyContent = 'center';
|
|
videoOverlay.style.alignItems = 'center';
|
|
break;
|
|
}
|
|
console.log('Overlay position set.');
|
|
}
|