Spaces:
Running
Running
Use fetch-based SSE with Authorization headers for private Space
Browse filesSwitch from EventSource to fetch with ReadableStream for SSE connection.
This allows adding Authorization headers which are required for private
HuggingFace Spaces.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- index.html +65 -42
index.html
CHANGED
|
@@ -221,7 +221,6 @@
|
|
| 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;
|
|
@@ -338,7 +337,10 @@
|
|
| 338 |
try {
|
| 339 |
const response = await fetch(`${SIGNALING_SERVER}/send?token=${encodeURIComponent(userToken)}`, {
|
| 340 |
method: 'POST',
|
| 341 |
-
headers: {
|
|
|
|
|
|
|
|
|
|
| 342 |
body: JSON.stringify(message)
|
| 343 |
});
|
| 344 |
|
|
@@ -354,8 +356,10 @@
|
|
| 354 |
}
|
| 355 |
}
|
| 356 |
|
| 357 |
-
// Signaling via SSE
|
| 358 |
-
|
|
|
|
|
|
|
| 359 |
if (!userToken) {
|
| 360 |
log('Not authenticated', 'error');
|
| 361 |
return;
|
|
@@ -365,56 +369,75 @@
|
|
| 365 |
log('Connecting to signaling server (SSE)...');
|
| 366 |
updateSignalingStatus('connecting');
|
| 367 |
|
|
|
|
|
|
|
| 368 |
try {
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 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 |
-
|
| 389 |
-
|
| 390 |
-
}
|
| 391 |
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
}
|
| 406 |
-
}
|
| 407 |
|
| 408 |
} catch (e) {
|
| 409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
updateSignalingStatus('disconnected');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
}
|
| 412 |
}
|
| 413 |
|
| 414 |
function disconnectSignaling() {
|
| 415 |
-
if (
|
| 416 |
-
|
| 417 |
-
|
| 418 |
}
|
| 419 |
updateSignalingStatus('disconnected');
|
| 420 |
document.getElementById('connectBtn').disabled = false;
|
|
|
|
| 221 |
const SIGNALING_SERVER = 'https://cduss-reachy-mini-central.hf.space';
|
| 222 |
|
| 223 |
// State
|
|
|
|
| 224 |
let peerConnection = null;
|
| 225 |
let dataChannel = null;
|
| 226 |
let selectedProducerId = null;
|
|
|
|
| 337 |
try {
|
| 338 |
const response = await fetch(`${SIGNALING_SERVER}/send?token=${encodeURIComponent(userToken)}`, {
|
| 339 |
method: 'POST',
|
| 340 |
+
headers: {
|
| 341 |
+
'Content-Type': 'application/json',
|
| 342 |
+
'Authorization': `Bearer ${userToken}`,
|
| 343 |
+
},
|
| 344 |
body: JSON.stringify(message)
|
| 345 |
});
|
| 346 |
|
|
|
|
| 356 |
}
|
| 357 |
}
|
| 358 |
|
| 359 |
+
// Signaling via SSE (using fetch for header support)
|
| 360 |
+
let sseAbortController = null;
|
| 361 |
+
|
| 362 |
+
async function connectSignaling() {
|
| 363 |
if (!userToken) {
|
| 364 |
log('Not authenticated', 'error');
|
| 365 |
return;
|
|
|
|
| 369 |
log('Connecting to signaling server (SSE)...');
|
| 370 |
updateSignalingStatus('connecting');
|
| 371 |
|
| 372 |
+
sseAbortController = new AbortController();
|
| 373 |
+
|
| 374 |
try {
|
| 375 |
+
const response = await fetch(url, {
|
| 376 |
+
method: 'GET',
|
| 377 |
+
headers: {
|
| 378 |
+
'Authorization': `Bearer ${userToken}`,
|
| 379 |
+
'Accept': 'text/event-stream',
|
| 380 |
+
},
|
| 381 |
+
signal: sseAbortController.signal,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
});
|
| 383 |
|
| 384 |
+
if (!response.ok) {
|
| 385 |
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
| 386 |
+
}
|
| 387 |
|
| 388 |
+
log('Connected to signaling server!', 'success');
|
| 389 |
+
updateSignalingStatus('connected');
|
| 390 |
+
document.getElementById('connectBtn').disabled = true;
|
| 391 |
+
document.getElementById('disconnectBtn').disabled = false;
|
| 392 |
+
|
| 393 |
+
// Read the SSE stream
|
| 394 |
+
const reader = response.body.getReader();
|
| 395 |
+
const decoder = new TextDecoder();
|
| 396 |
+
let buffer = '';
|
| 397 |
+
|
| 398 |
+
while (true) {
|
| 399 |
+
const { done, value } = await reader.read();
|
| 400 |
+
if (done) break;
|
| 401 |
+
|
| 402 |
+
buffer += decoder.decode(value, { stream: true });
|
| 403 |
+
const lines = buffer.split('\n');
|
| 404 |
+
buffer = lines.pop(); // Keep incomplete line in buffer
|
| 405 |
+
|
| 406 |
+
for (const line of lines) {
|
| 407 |
+
if (line.startsWith('data:')) {
|
| 408 |
+
const data = line.slice(5).trim();
|
| 409 |
+
if (data) {
|
| 410 |
+
try {
|
| 411 |
+
const msg = JSON.parse(data);
|
| 412 |
+
log(`Received: ${msg.type}`);
|
| 413 |
+
handleSignalingMessage(msg);
|
| 414 |
+
} catch (e) {
|
| 415 |
+
console.error('Failed to parse:', data, e);
|
| 416 |
+
}
|
| 417 |
+
}
|
| 418 |
+
}
|
| 419 |
}
|
| 420 |
+
}
|
| 421 |
|
| 422 |
} catch (e) {
|
| 423 |
+
if (e.name === 'AbortError') {
|
| 424 |
+
log('Disconnected', 'info');
|
| 425 |
+
} else {
|
| 426 |
+
log(`Connection error: ${e.message}`, 'error');
|
| 427 |
+
}
|
| 428 |
updateSignalingStatus('disconnected');
|
| 429 |
+
document.getElementById('connectBtn').disabled = false;
|
| 430 |
+
document.getElementById('disconnectBtn').disabled = true;
|
| 431 |
+
document.getElementById('startStreamBtn').disabled = true;
|
| 432 |
+
selectedProducerId = null;
|
| 433 |
+
myPeerId = null;
|
| 434 |
}
|
| 435 |
}
|
| 436 |
|
| 437 |
function disconnectSignaling() {
|
| 438 |
+
if (sseAbortController) {
|
| 439 |
+
sseAbortController.abort();
|
| 440 |
+
sseAbortController = null;
|
| 441 |
}
|
| 442 |
updateSignalingStatus('disconnected');
|
| 443 |
document.getElementById('connectBtn').disabled = false;
|