mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-18 02:20:47 -04:00
video player!
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
.rover-card h3 { margin: 0 0 8px 0; }
|
||||
.sensor { font-family: monospace; white-space: pre-wrap; word-break: break-word; }
|
||||
.meta { font-size: 0.9rem; color: #555; margin-bottom: 8px; }
|
||||
.video-wrap { background: #000; min-height: 160px; margin-bottom: 6px; display: flex; align-items: center; justify-content: center; }
|
||||
.video-wrap video { width: 100%; max-height: 200px; background: #000; }
|
||||
.video-status { font-size: 0.85rem; color: #444; margin-bottom: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -24,6 +27,7 @@
|
||||
</script>
|
||||
<script src="../loader.js"></script>
|
||||
<script src="../src/globals/socket.js"></script>
|
||||
<script src="../src/services/videoPlayer.js"></script>
|
||||
<script src="spectator.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
registerModule('spectator/main', (require, exports) => {
|
||||
const { socket } = require('globals/socket');
|
||||
const videoPlayer = require('services/videoPlayer');
|
||||
|
||||
const statusEl = document.getElementById('status');
|
||||
const grid = document.getElementById('grid');
|
||||
@@ -11,19 +12,23 @@ registerModule('spectator/main', (require, exports) => {
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
statusEl.textContent = 'Disconnected';
|
||||
stopAllVideos();
|
||||
});
|
||||
|
||||
socket.on('lockdown', ({ message }) => {
|
||||
statusEl.textContent = message || 'Spectator mode disabled';
|
||||
stopAllVideos();
|
||||
});
|
||||
|
||||
socket.on('rovers', (list) => {
|
||||
const seen = new Set();
|
||||
list.forEach((rover) => {
|
||||
const card = ensureCard(rover.id);
|
||||
card.querySelector('.meta').textContent = `Locked: ${rover.locked ? 'yes' : 'no'} | Last seen: ${new Date(rover.lastSeen).toLocaleTimeString()}`;
|
||||
if (!card.dataset.videoActive) {
|
||||
connectVideo(rover.id);
|
||||
}
|
||||
});
|
||||
cards.forEach((_, id) => {
|
||||
Array.from(cards.keys()).forEach((id) => {
|
||||
if (!list.find((r) => r.id === id)) {
|
||||
removeCard(id);
|
||||
}
|
||||
@@ -41,23 +46,85 @@ registerModule('spectator/main', (require, exports) => {
|
||||
}
|
||||
const card = document.createElement('div');
|
||||
card.className = 'rover-card';
|
||||
card.dataset.videoActive = '';
|
||||
card.innerHTML = `
|
||||
<h3>${roverId}</h3>
|
||||
<div class="meta"></div>
|
||||
<div class="video-wrap"></div>
|
||||
<div class="video-status">Video not connected</div>
|
||||
<button class="video-reconnect">Reconnect video</button>
|
||||
<pre class="sensor">Waiting for data...</pre>
|
||||
`;
|
||||
const reconnectBtn = card.querySelector('.video-reconnect');
|
||||
reconnectBtn.addEventListener('click', () => connectVideo(roverId, true));
|
||||
grid.appendChild(card);
|
||||
cards.set(roverId, card);
|
||||
connectVideo(roverId, true);
|
||||
return card;
|
||||
}
|
||||
|
||||
function connectVideo(roverId, force = false) {
|
||||
const card = ensureCard(roverId);
|
||||
const status = card.querySelector('.video-status');
|
||||
const wrap = card.querySelector('.video-wrap');
|
||||
const playerId = `spectator-${roverId}`;
|
||||
if (!force && card.dataset.videoActive === 'yes') {
|
||||
return;
|
||||
}
|
||||
status.textContent = 'Connecting video…';
|
||||
socket.emit('video:request', { roverId }, (resp = {}) => {
|
||||
if (resp.error) {
|
||||
status.textContent = `Video error: ${resp.error}`;
|
||||
videoPlayer.stopStream(playerId);
|
||||
card.dataset.videoActive = '';
|
||||
return;
|
||||
}
|
||||
videoPlayer
|
||||
.startStream(playerId, {
|
||||
url: resp.url,
|
||||
mount: wrap,
|
||||
onStatus: (state, detail) => {
|
||||
if (state === 'playing') {
|
||||
status.textContent = 'Video live';
|
||||
card.dataset.videoActive = 'yes';
|
||||
} else if (state === 'connecting') {
|
||||
status.textContent = 'Connecting video…';
|
||||
} else if (state === 'error') {
|
||||
status.textContent = `Video error: ${detail}`;
|
||||
card.dataset.videoActive = '';
|
||||
} else if (state === 'stopped') {
|
||||
status.textContent = 'Video stopped';
|
||||
card.dataset.videoActive = '';
|
||||
}
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
status.textContent = `Video error: ${err.message}`;
|
||||
card.dataset.videoActive = '';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeCard(roverId) {
|
||||
const card = cards.get(roverId);
|
||||
if (card) {
|
||||
videoPlayer.stopStream(`spectator-${roverId}`);
|
||||
card.remove();
|
||||
cards.delete(roverId);
|
||||
}
|
||||
}
|
||||
|
||||
function stopAllVideos() {
|
||||
Array.from(cards.keys()).forEach((id) => {
|
||||
videoPlayer.stopStream(`spectator-${id}`);
|
||||
const card = cards.get(id);
|
||||
if (card) {
|
||||
const status = card.querySelector('.video-status');
|
||||
status.textContent = 'Video stopped';
|
||||
card.dataset.videoActive = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
requireModule('spectator/main');
|
||||
|
||||
Reference in New Issue
Block a user