|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Personalized Avatar Video</title>
|
|
</head>
|
|
<body>
|
|
<h2>Personalized Avatar Video</h2>
|
|
<div>
|
|
<label>Upload or record audio: <input type="file" id="audioInput" accept="audio/*"></label>
|
|
<button id="btnTranscribe">Transcribe</button><br>
|
|
<label>Or enter text: <input type="text" id="textInput"></label>
|
|
<button id="btnUseText">Use Text</button><br>
|
|
<label>Transcription / Edited Text:</label><br>
|
|
<textarea id="transcribed" rows="2" cols="50"></textarea><br>
|
|
<label>Assistant Response:</label><br>
|
|
<textarea id="assistant" rows="2" cols="50"></textarea><br>
|
|
<button id="btnChat">Get Response</button>
|
|
</div>
|
|
<hr>
|
|
<div>
|
|
<label>Select Avatar:</label>
|
|
<select id="avatarSelect">
|
|
{% for name in avatars %}
|
|
<option value="{{ name }}">{{ name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<img id="avatarImg" src="" width="150" height="150">
|
|
</div>
|
|
<hr>
|
|
<div>
|
|
<button id="btnTTS">Synthesize Speech</button><br>
|
|
<audio id="audioOut" controls autoplay></audio><br>
|
|
<button id="btnSync">Generate Lip-Synced Video</button><br>
|
|
<video id="videoOut" width="400" controls autoplay muted></video>
|
|
</div>
|
|
<script>
|
|
async function requestMic() {
|
|
try {
|
|
await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
} catch(e) {
|
|
console.warn('Microphone access denied');
|
|
}
|
|
}
|
|
window.addEventListener('load', requestMic);
|
|
|
|
document.getElementById('avatarSelect').addEventListener('change',function(){
|
|
var n=this.value;
|
|
document.getElementById('avatarImg').src='/thumbnails/'+n+'.png';
|
|
});
|
|
document.getElementById('btnTranscribe').onclick = async function(){
|
|
var inp = document.getElementById('audioInput');
|
|
if(!inp.files.length) return;
|
|
var fd = new FormData();
|
|
fd.append('audio', inp.files[0]);
|
|
var res = await fetch('/transcribe',{method:'POST',body:fd});
|
|
var j = await res.json();
|
|
document.getElementById('transcribed').value = j.text;
|
|
};
|
|
document.getElementById('btnUseText').onclick = function(){
|
|
document.getElementById('transcribed').value = document.getElementById('textInput').value;
|
|
};
|
|
document.getElementById('btnChat').onclick = async function(){
|
|
var text = document.getElementById('transcribed').value;
|
|
var res = await fetch('/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({text})});
|
|
var j = await res.json();
|
|
document.getElementById('assistant').value = j.response;
|
|
};
|
|
document.getElementById('btnTTS').onclick = async function(){
|
|
var text = document.getElementById('assistant').value;
|
|
var res = await fetch('/tts',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({text})});
|
|
var j = await res.json();
|
|
if(j.audio_url) {
|
|
var a = document.getElementById('audioOut');
|
|
a.src = j.audio_url;
|
|
await a.play();
|
|
}
|
|
};
|
|
document.getElementById('btnSync').onclick = async function(){
|
|
var avatar = document.getElementById('avatarSelect').value;
|
|
var audio_url = document.getElementById('audioOut').src;
|
|
var res = await fetch('/sync',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({avatar,audio_url})});
|
|
var j = await res.json();
|
|
if(j.video_url) {
|
|
var v = document.getElementById('videoOut');
|
|
v.src = j.video_url;
|
|
v.muted = false;
|
|
await v.play();
|
|
}
|
|
};
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|