| |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>AI Voice Assistant</title> |
| <style> |
| body { |
| font-family: Arial, sans-serif; |
| max-width: 800px; |
| margin: 0 auto; |
| padding: 20px; |
| } |
| .conversation { |
| margin-top: 20px; |
| border: 1px solid #ccc; |
| padding: 20px; |
| height: 400px; |
| overflow-y: auto; |
| } |
| .status { |
| margin-top: 10px; |
| color: #666; |
| } |
| .user-message { |
| color: blue; |
| margin: 10px 0; |
| } |
| .assistant-message { |
| color: green; |
| margin: 10px 0; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>AI Voice Assistant</h1> |
| <div class="status" id="status">Initializing...</div> |
| <div class="conversation" id="conversation"></div> |
|
|
| <script> |
| let mediaRecorder; |
| let socket; |
| let audioChunks = []; |
| |
| function initializeAudio() { |
| function connect() { |
| socket = new WebSocket(`wss://${window.location.host}/ws`); |
| |
| socket.onopen = () => { |
| document.getElementById('status').textContent = 'Connected. Listening... (Say "Computer" to activate)'; |
| }; |
| |
| socket.onmessage = (event) => { |
| try { |
| const data = JSON.parse(event.data); |
| |
| if (data.user_text) { |
| addMessage('user', data.user_text); |
| addMessage('assistant', `German: ${data.response_de}\nEnglish: ${data.response_en}`); |
| } |
| |
| if (data.audio) { |
| |
| const audio = new Audio(URL.createObjectURL( |
| new Blob([new Uint8Array(atob(data.audio).split('').map(c => c.charCodeAt(0)))], |
| { type: 'audio/wav' }) |
| )); |
| audio.play(); |
| } |
| } catch (error) { |
| console.error('Error parsing WebSocket message:', error); |
| } |
| }; |
| |
| socket.onclose = (event) => { |
| document.getElementById('status').textContent = 'Disconnected. Reconnecting...'; |
| setTimeout(connect, 1000); |
| }; |
| |
| socket.onerror = (error) => { |
| console.error('WebSocket error:', error); |
| document.getElementById('status').textContent = 'Connection error'; |
| }; |
| } |
| |
| connect(); |
| |
| |
| mediaRecorder.ondataavailable = (event) => { |
| if (event.data.size > 0) { |
| const reader = new FileReader(); |
| reader.onloadend = () => { |
| |
| if (socket.readyState === WebSocket.OPEN) { |
| socket.send(reader.result); |
| } |
| }; |
| reader.readAsArrayBuffer(event.data); |
| } |
| }; |
| |
| mediaRecorder.start(480); |
| } |
| function addMessage(sender, text) { |
| const conversation = document.getElementById('conversation'); |
| const message = document.createElement('div'); |
| message.className = sender === 'user' ? 'user-message' : 'assistant-message'; |
| message.textContent = text; |
| conversation.appendChild(message); |
| conversation.scrollTop = conversation.scrollHeight; |
| } |
| |
| |
| window.addEventListener('load', initializeAudio); |
| |
| |
| </script> |
| </body> |
| </html> |