cduss commited on
Commit
83671f4
·
1 Parent(s): b293436

Switch from WebSocket to SSE+HTTP for browser compatibility

Browse files
Files changed (1) hide show
  1. index.html +81 -39
index.html CHANGED
@@ -217,11 +217,11 @@
217
  <script type="module">
218
  import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.15.2/+esm";
219
 
220
- // Central signaling server (same HF space owner)
221
- const SIGNALING_SERVER = 'wss://cduss-reachy-mini-central.hf.space/ws';
222
 
223
  // State
224
- let signalingWs = null;
225
  let peerConnection = null;
226
  let dataChannel = null;
227
  let selectedProducerId = null;
@@ -328,50 +328,83 @@
328
  document.getElementById('logArea').innerHTML = '';
329
  }
330
 
331
- // Signaling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  function connectSignaling() {
333
  if (!userToken) {
334
  log('Not authenticated', 'error');
335
  return;
336
  }
337
 
338
- const url = `${SIGNALING_SERVER}?token=${encodeURIComponent(userToken)}`;
339
- log('Connecting to signaling server...');
340
  updateSignalingStatus('connecting');
341
 
342
  try {
343
- signalingWs = new WebSocket(url);
344
 
345
- signalingWs.onopen = () => {
346
  log('Connected to signaling server!', 'success');
347
  updateSignalingStatus('connected');
348
  document.getElementById('connectBtn').disabled = true;
349
  document.getElementById('disconnectBtn').disabled = false;
350
  };
351
 
352
- signalingWs.onmessage = (event) => {
353
- const msg = JSON.parse(event.data);
354
- log(`Received: ${msg.type}`);
355
- handleSignalingMessage(msg);
356
- };
 
 
 
 
357
 
358
- signalingWs.onerror = (error) => {
359
- log('WebSocket error', 'error');
360
- };
361
 
362
- signalingWs.onclose = (event) => {
363
- log(`Disconnected: code=${event.code}`);
364
- if (event.code === 4001) {
365
- log('Authentication failed - please sign in again', 'error');
366
- logout();
367
- }
368
  updateSignalingStatus('disconnected');
369
  document.getElementById('connectBtn').disabled = false;
370
  document.getElementById('disconnectBtn').disabled = true;
371
  document.getElementById('startStreamBtn').disabled = true;
372
  selectedProducerId = null;
373
  myPeerId = null;
 
 
 
 
 
 
374
  };
 
375
  } catch (e) {
376
  log(`Failed to connect: ${e.message}`, 'error');
377
  updateSignalingStatus('disconnected');
@@ -379,10 +412,13 @@
379
  }
380
 
381
  function disconnectSignaling() {
382
- if (signalingWs) {
383
- signalingWs.close();
384
- signalingWs = null;
385
  }
 
 
 
386
  stopStream();
387
  }
388
 
@@ -393,19 +429,17 @@
393
  el.textContent = status.charAt(0).toUpperCase() + status.slice(1);
394
  }
395
 
396
- function handleSignalingMessage(msg) {
397
  switch (msg.type) {
398
  case 'welcome':
399
  myPeerId = msg.peerId;
400
  log(`Connected as: ${myPeerId.substring(0, 8)}...`, 'success');
401
- // Register as listener to get producer announcements
402
- signalingWs.send(JSON.stringify({
403
  type: 'setPeerStatus',
404
  roles: ['listener'],
405
  meta: { name: 'WebRTC Dashboard' }
406
- }));
407
- // Request list of producers
408
- signalingWs.send(JSON.stringify({ type: 'list' }));
409
  break;
410
 
411
  case 'list':
@@ -415,7 +449,10 @@
415
  case 'peerStatusChanged':
416
  log(`Robot ${msg.peerId.substring(0, 8)}... ${msg.roles?.length ? 'connected' : 'disconnected'}`);
417
  // Refresh producer list
418
- signalingWs.send(JSON.stringify({ type: 'list' }));
 
 
 
419
  break;
420
 
421
  case 'sessionStarted':
@@ -488,9 +525,9 @@
488
  }
489
  };
490
 
491
- peerConnection.onicecandidate = (event) => {
492
  if (event.candidate && currentSessionId) {
493
- signalingWs.send(JSON.stringify({
494
  type: 'peer',
495
  sessionId: currentSessionId,
496
  ice: {
@@ -498,7 +535,7 @@
498
  sdpMLineIndex: event.candidate.sdpMLineIndex,
499
  sdpMid: event.candidate.sdpMid
500
  }
501
- }));
502
  }
503
  };
504
 
@@ -525,10 +562,15 @@
525
  };
526
 
527
  log('Requesting session with robot...');
528
- signalingWs.send(JSON.stringify({
529
  type: 'startSession',
530
  peerId: selectedProducerId
531
- }));
 
 
 
 
 
532
  }
533
 
534
  async function handlePeerMessage(msg) {
@@ -542,11 +584,11 @@
542
  if (msg.sdp.type === 'offer') {
543
  const answer = await peerConnection.createAnswer();
544
  await peerConnection.setLocalDescription(answer);
545
- signalingWs.send(JSON.stringify({
546
  type: 'peer',
547
  sessionId: currentSessionId,
548
  sdp: { type: 'answer', sdp: answer.sdp }
549
- }));
550
  log('Sent SDP answer');
551
  }
552
  }
 
217
  <script type="module">
218
  import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.15.2/+esm";
219
 
220
+ // Central signaling server (HTTP/SSE instead of WebSocket)
221
+ const SIGNALING_SERVER = 'https://cduss-reachy-mini-central.hf.space';
222
 
223
  // State
224
+ let eventSource = null;
225
  let peerConnection = null;
226
  let dataChannel = null;
227
  let selectedProducerId = null;
 
328
  document.getElementById('logArea').innerHTML = '';
329
  }
330
 
331
+ // Send message to server via HTTP POST
332
+ async function sendToServer(message) {
333
+ if (!userToken) {
334
+ log('Not authenticated', 'error');
335
+ return null;
336
+ }
337
+
338
+ try {
339
+ const response = await fetch(`${SIGNALING_SERVER}/send?token=${encodeURIComponent(userToken)}`, {
340
+ method: 'POST',
341
+ headers: { 'Content-Type': 'application/json' },
342
+ body: JSON.stringify(message)
343
+ });
344
+
345
+ if (!response.ok) {
346
+ log(`Server error: ${response.status}`, 'error');
347
+ return null;
348
+ }
349
+
350
+ return await response.json();
351
+ } catch (e) {
352
+ log(`Failed to send: ${e.message}`, 'error');
353
+ return null;
354
+ }
355
+ }
356
+
357
+ // Signaling via SSE
358
  function connectSignaling() {
359
  if (!userToken) {
360
  log('Not authenticated', 'error');
361
  return;
362
  }
363
 
364
+ const url = `${SIGNALING_SERVER}/events?token=${encodeURIComponent(userToken)}`;
365
+ log('Connecting to signaling server (SSE)...');
366
  updateSignalingStatus('connecting');
367
 
368
  try {
369
+ eventSource = new EventSource(url);
370
 
371
+ eventSource.onopen = () => {
372
  log('Connected to signaling server!', 'success');
373
  updateSignalingStatus('connected');
374
  document.getElementById('connectBtn').disabled = true;
375
  document.getElementById('disconnectBtn').disabled = false;
376
  };
377
 
378
+ eventSource.addEventListener('message', (event) => {
379
+ try {
380
+ const msg = JSON.parse(event.data);
381
+ log(`Received: ${msg.type}`);
382
+ handleSignalingMessage(msg);
383
+ } catch (e) {
384
+ console.error('Failed to parse message:', e);
385
+ }
386
+ });
387
 
388
+ eventSource.addEventListener('ping', () => {
389
+ // Keepalive, ignore
390
+ });
391
 
392
+ eventSource.onerror = (error) => {
393
+ log('Connection error', 'error');
 
 
 
 
394
  updateSignalingStatus('disconnected');
395
  document.getElementById('connectBtn').disabled = false;
396
  document.getElementById('disconnectBtn').disabled = true;
397
  document.getElementById('startStreamBtn').disabled = true;
398
  selectedProducerId = null;
399
  myPeerId = null;
400
+
401
+ // Auto-reconnect after 5 seconds
402
+ if (eventSource) {
403
+ eventSource.close();
404
+ eventSource = null;
405
+ }
406
  };
407
+
408
  } catch (e) {
409
  log(`Failed to connect: ${e.message}`, 'error');
410
  updateSignalingStatus('disconnected');
 
412
  }
413
 
414
  function disconnectSignaling() {
415
+ if (eventSource) {
416
+ eventSource.close();
417
+ eventSource = null;
418
  }
419
+ updateSignalingStatus('disconnected');
420
+ document.getElementById('connectBtn').disabled = false;
421
+ document.getElementById('disconnectBtn').disabled = true;
422
  stopStream();
423
  }
424
 
 
429
  el.textContent = status.charAt(0).toUpperCase() + status.slice(1);
430
  }
431
 
432
+ async function handleSignalingMessage(msg) {
433
  switch (msg.type) {
434
  case 'welcome':
435
  myPeerId = msg.peerId;
436
  log(`Connected as: ${myPeerId.substring(0, 8)}...`, 'success');
437
+ // Register as listener
438
+ await sendToServer({
439
  type: 'setPeerStatus',
440
  roles: ['listener'],
441
  meta: { name: 'WebRTC Dashboard' }
442
+ });
 
 
443
  break;
444
 
445
  case 'list':
 
449
  case 'peerStatusChanged':
450
  log(`Robot ${msg.peerId.substring(0, 8)}... ${msg.roles?.length ? 'connected' : 'disconnected'}`);
451
  // Refresh producer list
452
+ const listResponse = await sendToServer({ type: 'list' });
453
+ if (listResponse && listResponse.producers) {
454
+ displayProducers(listResponse.producers);
455
+ }
456
  break;
457
 
458
  case 'sessionStarted':
 
525
  }
526
  };
527
 
528
+ peerConnection.onicecandidate = async (event) => {
529
  if (event.candidate && currentSessionId) {
530
+ await sendToServer({
531
  type: 'peer',
532
  sessionId: currentSessionId,
533
  ice: {
 
535
  sdpMLineIndex: event.candidate.sdpMLineIndex,
536
  sdpMid: event.candidate.sdpMid
537
  }
538
+ });
539
  }
540
  };
541
 
 
562
  };
563
 
564
  log('Requesting session with robot...');
565
+ const response = await sendToServer({
566
  type: 'startSession',
567
  peerId: selectedProducerId
568
+ });
569
+
570
+ if (response && response.sessionId) {
571
+ currentSessionId = response.sessionId;
572
+ log(`Session started: ${response.sessionId.substring(0, 8)}...`, 'success');
573
+ }
574
  }
575
 
576
  async function handlePeerMessage(msg) {
 
584
  if (msg.sdp.type === 'offer') {
585
  const answer = await peerConnection.createAnswer();
586
  await peerConnection.setLocalDescription(answer);
587
+ await sendToServer({
588
  type: 'peer',
589
  sessionId: currentSessionId,
590
  sdp: { type: 'answer', sdp: answer.sdp }
591
+ });
592
  log('Sent SDP answer');
593
  }
594
  }