|
|
|
<!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> |