qfuxa commited on
Commit
89e3334
·
1 Parent(s): 4ae7cba

start implementing frontend part of https://github.com/QuentinFuxa/WhisperLiveKit/pull/80

Browse files
whisperlivekit/web/live_transcription.html CHANGED
@@ -38,7 +38,6 @@
38
  transform: scale(0.95);
39
  }
40
 
41
- /* Shape inside the button */
42
  .shape-container {
43
  width: 25px;
44
  height: 25px;
@@ -56,6 +55,10 @@
56
  transition: all 0.3s ease;
57
  }
58
 
 
 
 
 
59
  #recordButton.recording .shape {
60
  border-radius: 5px;
61
  width: 25px;
@@ -304,6 +307,7 @@
304
  let waveCanvas = document.getElementById("waveCanvas");
305
  let waveCtx = waveCanvas.getContext("2d");
306
  let animationFrame = null;
 
307
  waveCanvas.width = 60 * (window.devicePixelRatio || 1);
308
  waveCanvas.height = 30 * (window.devicePixelRatio || 1);
309
  waveCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
@@ -346,10 +350,16 @@
346
 
347
  websocket.onclose = () => {
348
  if (userClosing) {
349
- statusText.textContent = "WebSocket closed by user.";
 
 
 
350
  } else {
351
  statusText.textContent =
352
  "Disconnected from the WebSocket server. (Check logs if model is loading.)";
 
 
 
353
  }
354
  userClosing = false;
355
  };
@@ -363,6 +373,27 @@
363
  websocket.onmessage = (event) => {
364
  const data = JSON.parse(event.data);
365
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  const {
367
  lines = [],
368
  buffer_transcription = "",
@@ -494,8 +525,17 @@
494
  }
495
  }
496
 
497
- function stopRecording() {
498
  userClosing = true;
 
 
 
 
 
 
 
 
 
499
  if (recorder) {
500
  recorder.stop();
501
  recorder = null;
@@ -531,34 +571,67 @@
531
  timerElement.textContent = "00:00";
532
  startTime = null;
533
 
534
- isRecording = false;
535
-
536
- if (websocket) {
537
- websocket.close();
538
- websocket = null;
 
 
 
 
 
 
 
 
539
  }
540
-
 
541
  updateUI();
542
  }
543
 
544
  async function toggleRecording() {
545
  if (!isRecording) {
546
- linesTranscriptDiv.innerHTML = "";
 
 
 
 
547
  try {
548
- await setupWebSocket();
549
- await startRecording();
 
 
 
 
 
 
550
  } catch (err) {
551
  statusText.textContent = "Could not connect to WebSocket or access mic. Aborted.";
552
  console.error(err);
553
  }
554
  } else {
 
555
  stopRecording();
556
  }
557
  }
558
 
559
  function updateUI() {
560
  recordButton.classList.toggle("recording", isRecording);
561
- statusText.textContent = isRecording ? "Recording..." : "Click to start transcription";
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  }
563
 
564
  recordButton.addEventListener("click", toggleRecording);
 
38
  transform: scale(0.95);
39
  }
40
 
 
41
  .shape-container {
42
  width: 25px;
43
  height: 25px;
 
55
  transition: all 0.3s ease;
56
  }
57
 
58
+ #recordButton:disabled .shape {
59
+ background-color: #6e6d6d;
60
+ }
61
+
62
  #recordButton.recording .shape {
63
  border-radius: 5px;
64
  width: 25px;
 
307
  let waveCanvas = document.getElementById("waveCanvas");
308
  let waveCtx = waveCanvas.getContext("2d");
309
  let animationFrame = null;
310
+ let waitingForStop = false;
311
  waveCanvas.width = 60 * (window.devicePixelRatio || 1);
312
  waveCanvas.height = 30 * (window.devicePixelRatio || 1);
313
  waveCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
 
350
 
351
  websocket.onclose = () => {
352
  if (userClosing) {
353
+ if (!statusText.textContent.includes("Recording stopped. Processing final audio")) { // This is a bit of a hack. We should have a better way to handle this. eg. using a status code.
354
+ statusText.textContent = "Finished processing audio! Ready to record again.";
355
+ }
356
+ waitingForStop = false;
357
  } else {
358
  statusText.textContent =
359
  "Disconnected from the WebSocket server. (Check logs if model is loading.)";
360
+ if (isRecording) {
361
+ stopRecording();
362
+ }
363
  }
364
  userClosing = false;
365
  };
 
373
  websocket.onmessage = (event) => {
374
  const data = JSON.parse(event.data);
375
 
376
+ // Check for status messages
377
+ if (data.type === "ready_to_stop") {
378
+ console.log("Ready to stop, closing WebSocket");
379
+
380
+ // signal that we are not waiting for stop anymore
381
+ waitingForStop = false;
382
+ recordButton.disabled = false; // this should be elsewhere
383
+ console.log("Record button enabled");
384
+
385
+ //Now we can close the WebSocket
386
+ if (websocket) {
387
+ websocket.close();
388
+ websocket = null;
389
+ }
390
+
391
+
392
+
393
+ return;
394
+ }
395
+
396
+ // Handle normal transcription updates
397
  const {
398
  lines = [],
399
  buffer_transcription = "",
 
525
  }
526
  }
527
 
528
+ async function stopRecording() {
529
  userClosing = true;
530
+ waitingForStop = true;
531
+
532
+ if (websocket && websocket.readyState === WebSocket.OPEN) {
533
+ // Send empty audio buffer as stop signal
534
+ const emptyBlob = new Blob([], { type: 'audio/webm' });
535
+ websocket.send(emptyBlob);
536
+ statusText.textContent = "Recording stopped. Processing final audio...";
537
+ }
538
+
539
  if (recorder) {
540
  recorder.stop();
541
  recorder = null;
 
571
  timerElement.textContent = "00:00";
572
  startTime = null;
573
 
574
+ if (websocket && websocket.readyState === WebSocket.OPEN) {
575
+ try {
576
+ await websocket.send(JSON.stringify({
577
+ type: "stop",
578
+ message: "User stopped recording"
579
+ }));
580
+ statusText.textContent = "Recording stopped. Processing final audio...";
581
+ } catch (e) {
582
+ console.error("Could not send stop message:", e);
583
+ statusText.textContent = "Recording stopped. Error during final audio processing.";
584
+ websocket.close();
585
+ websocket = null;
586
+ }
587
  }
588
+
589
+ isRecording = false;
590
  updateUI();
591
  }
592
 
593
  async function toggleRecording() {
594
  if (!isRecording) {
595
+ if (waitingForStop) {
596
+ console.log("Waiting for stop, early return");
597
+ return; // Early return, UI is already updated
598
+ }
599
+ console.log("Connecting to WebSocket");
600
  try {
601
+ // If we have an active WebSocket that's still processing, just restart audio capture
602
+ if (websocket && websocket.readyState === WebSocket.OPEN) {
603
+ await startRecording();
604
+ } else {
605
+ // If no active WebSocket or it's closed, create new one
606
+ await setupWebSocket();
607
+ await startRecording();
608
+ }
609
  } catch (err) {
610
  statusText.textContent = "Could not connect to WebSocket or access mic. Aborted.";
611
  console.error(err);
612
  }
613
  } else {
614
+ console.log("Stopping recording");
615
  stopRecording();
616
  }
617
  }
618
 
619
  function updateUI() {
620
  recordButton.classList.toggle("recording", isRecording);
621
+
622
+ if (waitingForStop) {
623
+ statusText.textContent = "Please wait for processing to complete...";
624
+ recordButton.disabled = true; // Optionally disable the button while waiting
625
+ console.log("Record button disabled");
626
+ } else if (isRecording) {
627
+ statusText.textContent = "Recording...";
628
+ recordButton.disabled = false;
629
+ console.log("Record button enabled");
630
+ } else {
631
+ statusText.textContent = "Click to start transcription";
632
+ recordButton.disabled = false;
633
+ console.log("Record button enabled");
634
+ }
635
  }
636
 
637
  recordButton.addEventListener("click", toggleRecording);