|
|
|
|
|
import json |
|
from flask import Flask, request, send_file, url_for |
|
from threading import Lock |
|
import util |
|
import chat_util |
|
import os |
|
import requests |
|
import time |
|
import sys |
|
from flask import render_template |
|
|
|
|
|
library_path = util.ROOT_DIR + "/code/wav2lip_inference" |
|
print(library_path) |
|
sys.path.insert(1, library_path) |
|
from Wav2Lip import Processor |
|
|
|
|
|
from dotenv import load_dotenv |
|
|
|
load_dotenv(util.ROOT_DIR + "/.env") |
|
|
|
|
|
|
|
util.initialize() |
|
util.start_log_task("Initializing Flask app...") |
|
app = Flask(__name__) |
|
util.end_log_task() |
|
|
|
patient_agent = chat_util.generate_blank_patient() |
|
|
|
|
|
CHUNK_SIZE = 1024 |
|
BASE_URL = 'http://localhost:5000' |
|
|
|
@app.route('/', methods=['POST']) |
|
def index(): |
|
return "Homepage!" |
|
|
|
|
|
from subprocess import run, PIPE |
|
|
|
from flask import logging, Flask, render_template, request |
|
|
|
import wave, keyboard, faster_whisper, torch.cuda |
|
|
|
model, answer, history = faster_whisper.WhisperModel(model_size_or_path="tiny.en", |
|
device='cuda' if torch.cuda.is_available() else 'cpu'), "", [] |
|
|
|
|
|
|
|
|
|
from threading import Thread |
|
|
|
|
|
@app.route('/client_test', methods=['GET']) |
|
def client_test(): |
|
return render_template('client.html') |
|
|
|
|
|
@app.route('/receive_audio', methods=['POST']) |
|
def receive_audio(): |
|
|
|
|
|
save_path = "temp/temp.webm" |
|
wav_save_path = 'temp/temp.wav' |
|
request.files['audio_file'].save(save_path) |
|
|
|
Thread(target=transcribe_text).start() |
|
|
|
return "Received audio file" |
|
|
|
|
|
@app.route('/transcribe_text', methods=['POST']) |
|
def transcribe_text(): |
|
|
|
save_path = "temp/temp.webm" |
|
wav_save_path = "temp/temp.wav" |
|
print("converting to wave audio") |
|
|
|
|
|
run(['ffmpeg', '-y', save_path, wav_save_path], stdout=PIPE, stderr=PIPE) |
|
|
|
print('preparing for transcription') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print('transcribing') |
|
user_text = " ".join(seg.text for seg in model.transcribe(wav_save_path, language="en")[0]) |
|
print(f'>>>{user_text}\n<<< ', end="", flush=True) |
|
return user_text |
|
|
|
|
|
|
|
@app.route('/generate_patient', methods=['POST']) |
|
def request_patient_generation(): |
|
|
|
global patient_agent |
|
|
|
patient_agent = chat_util.generate_patient(language_model_name='gpt-4-turbo-preview') |
|
return f"Generated patient agent ({patient_agent.name}) using {patient_agent.model.model_name} and the following system message: {patient_agent.system_message}" |
|
|
|
|
|
@app.route('/generate_patient_text', methods=['POST']) |
|
def generate_patient_text(message_from_user=None): |
|
|
|
if not message_from_user: |
|
message_from_user = request.json['message_from_user'] |
|
util.rprint(f"[bold]Conversation started [/bold] \n βββββββββββββββββββββββββββββββββββββββββββ ") |
|
util.rprint(f" [blue][bold]βΆ CLINICIAN [/bold] {message_from_user} [/blue]\n") |
|
patient_agent.receive(name="Clinician", message=message_from_user) |
|
message_from_patient = patient_agent.send() |
|
util.rprint(f" [cyan3][bold]βΆ PATIENT [/bold] {message_from_patient} [/cyan3]\n") |
|
return json.dumps({'message_from_patient': message_from_patient}) |
|
|
|
|
|
@app.route('/generate_patient_audio', methods=['POST']) |
|
def generate_patient_audio(message_from_user=None): |
|
request_id = util.generate_hash() |
|
|
|
if not message_from_user: |
|
message_from_user = request.json['message_from_user'] |
|
patient_text_response = json.loads(generate_patient_text(message_from_user))['message_from_patient'] |
|
url = "https://api.elevenlabs.io/v1/text-to-speech/jwnLlmJUpWazVNZOyzKE" |
|
querystring = {"optimize_streaming_latency": "4", "output_format": "mp3_44100_32"} |
|
payload = {"text": patient_text_response} |
|
headers = { |
|
"xi-api-key": os.environ["ELEVENLABS_API_KEY"], |
|
"Content-Type": "application/json" |
|
} |
|
util.start_log_task("Sending audio_files request to Eleven Labs...") |
|
response = requests.request("POST", url, json=payload, headers=headers, params=querystring) |
|
util.end_log_task() |
|
|
|
local_filename = request_id + '.mp3' |
|
util.log_task(f"Received {local_filename} from Eleven Labs.") |
|
filename = util.ROOT_DIR + '/audio_files/' + local_filename |
|
|
|
with open(filename, 'wb') as f: |
|
for chunk in response.iter_content(chunk_size=CHUNK_SIZE): |
|
if chunk: |
|
f.write(chunk) |
|
|
|
if response.status_code == 200: |
|
return json.dumps({'status': 'success', |
|
'request_id': request_id, |
|
'audio_url': BASE_URL + '/get_audio?filename=' + local_filename, |
|
'audio_path': filename, |
|
'message_from_patient': patient_text_response}) |
|
|
|
|
|
@app.route('/generate_remote_video', methods=['POST']) |
|
def generate_remote_video(audio_path=None): |
|
if not audio_path: |
|
audio_path = request.json['audio_path'] |
|
|
|
url = "https://api.synclabs.so/lipsync" |
|
querystring = {"optimize_streaming_latency": "4", "output_format": "mp3_44100_32"} |
|
payload = { |
|
"audioUrl": "https://cdn.syntheticpatients.org/audio/output_2024-04-30-T-01-47-46___72537b5cb2024fc3.mp3", |
|
"videoUrl": "https://cdn.syntheticpatients.org/video/alfarsi_speaking_shortly_5s_720p.mp4", |
|
"synergize": True, |
|
"maxCredits": None, |
|
"webhookUrl": None, |
|
"model": "wav2lip++" |
|
} |
|
headers = { |
|
"accept": "application/json", |
|
"x-api-key": os.environ["SYNCLABS_API_KEY"], |
|
"Content-Type": "application/json" |
|
} |
|
util.start_log_task("Sending video request to Sync Labs...") |
|
response = requests.request("POST", url, json=payload, headers=headers, params=querystring) |
|
util.end_log_task() |
|
|
|
print(response.text) |
|
|
|
video_sync_labs_id = json.loads(response.text)["id"] |
|
video_generating = True |
|
video_url = None |
|
while video_generating: |
|
url = f"https://api.synclabs.so/lipsync/{video_sync_labs_id}" |
|
headers = { |
|
"accept": "application/json", |
|
"x-api-key": os.environ["SYNCLABS_API_KEY"] |
|
} |
|
response = requests.request("GET", url, headers=headers) |
|
status = json.loads(response.text)["status"] |
|
if status == "COMPLETED": |
|
video_url = json.loads(response.text)["videoUrl"] |
|
util.lp("Video generation completed. Available at: " + video_url) |
|
video_generating = False |
|
else: |
|
util.lp("Video generation in progress. Status: " + status) |
|
time.sleep(5) |
|
return video_url |
|
|
|
|
|
@app.route('/generate_local_video', methods=['POST']) |
|
def generate_local_video(request_id=None): |
|
if not request_id: |
|
request_id = request.json['request_id'] |
|
|
|
audio_path = util.ROOT_DIR + '/audio_files/' + request_id + '.mp3' |
|
video_path = util.ROOT_DIR + '/video/trimmed.mp4' |
|
output_path = util.ROOT_DIR + '/video_output/' + request_id + '.mp4' |
|
output_url = BASE_URL + '/get_video?filename=' + request_id + '.mp4' |
|
|
|
util.log_mini_task("audio_files path: " + audio_path) |
|
util.log_mini_task("video path: " + video_path) |
|
|
|
processor.run(video_path, audio_path, output_path, resize_factor=1) |
|
|
|
return json.dumps({'status': 'success', |
|
'request_id': request_id, |
|
'video_url': output_url, |
|
'video_path': output_path}) |
|
|
|
|
|
|
|
|
|
@app.route('/get_audio', methods=['GET']) |
|
def get_audio_file(filename=None): |
|
if filename is None: |
|
filename = request.args.get('filename') |
|
return send_file(util.ROOT_DIR + '/audio_files/' + filename, as_attachment=True) |
|
|
|
|
|
@app.route('/get_video', methods=['GET']) |
|
def get_video_file(filename=None): |
|
if filename is None: |
|
filename = request.args.get('filename') |
|
return send_file(util.ROOT_DIR + '/video_output/' + filename, as_attachment=True) |
|
|
|
|
|
|
|
|
|
@app.route('/get_video_from_text', methods=['POST']) |
|
def get_video_from_text(message_from_user=None): |
|
if not message_from_user: |
|
message_from_user = request.json['message_from_user'] |
|
audio_response_text = generate_patient_audio(message_from_user) |
|
audio_response = json.loads(audio_response_text) |
|
util.log_mini_task("audio_files response: " + audio_response_text) |
|
|
|
|
|
request_id = audio_response['request_id'] |
|
video_response = json.loads(generate_local_video(request_id)) |
|
return json.dumps({'status': 'success', |
|
'request_id': request_id, |
|
'video_url': video_response['video_url'], |
|
'audio_url': audio_response['audio_url'], |
|
'message_from_patient': audio_response['message_from_patient']}) |
|
|
|
|
|
@app.route('/client', methods=['GET']) |
|
def client(message_from_user=None): |
|
client_html = ''' |
|
<html lang="en"><head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Video Chat</title> |
|
|
|
|
|
|
|
<style> |
|
|
|
#submit_chat:hover {background-color: #3c3c3c;} |
|
|
|
#submit_chat { |
|
background-color: black; |
|
} |
|
|
|
#submit_chat:active { |
|
background-color: #2f8383; |
|
} |
|
</style> |
|
|
|
</head> |
|
<body style=" |
|
margin: 0px; |
|
"> |
|
<div> |
|
<video id="videoPlayer" width="100%" class="active-video sp-video" src="" style="position: absolute;width: 100%;" autoplay="" muted=""></video> |
|
<video id="idleVideoPlayer" class="idle-video sp-video" src="http://localhost:5000/get_video?filename=idle_high-res_720_26s_adapter.webm" autoplay="" loop="" muted="" style=" |
|
width: 100%; |
|
"></video> |
|
|
|
|
|
|
|
</div> |
|
<div style=" |
|
"> |
|
<form id="chatForm" style=" |
|
display: flex; |
|
flex-direction: column; |
|
"> |
|
|
|
<textarea type="textarea" id="userMessage" name="userMessage" style=" |
|
border: 1px solid black; |
|
background: rgba(239, 239, 239, 1); |
|
min-height: 100px; |
|
font-family: 'IBM Plex Mono', monospace; |
|
padding: 10px; |
|
resize: none; |
|
"> Enter your message here. </textarea> |
|
|
|
<div id="loading_overlay" name="loading_overlay" style=" |
|
min-height: 152px; |
|
font-family: 'IBM Plex Mono', monospace; |
|
position: absolute; |
|
width: 100%; |
|
display: none; |
|
background: rgba(220, 220, 220, 0.8); |
|
"> Loading... </div> |
|
|
|
|
|
<input type="submit" id="submit_chat" value="Submit" style=" |
|
min-width: 119px; |
|
color: white; |
|
font-family: 'IBM Plex Mono',monospace; |
|
padding: 15px; |
|
"> |
|
</form> |
|
</div> |
|
|
|
<script> |
|
|
|
function sleep(ms) { |
|
return new Promise(resolve => setTimeout(resolve, ms)); |
|
} |
|
|
|
async function example(ms) { |
|
console.log('Start'); |
|
await sleep(ms); |
|
console.log('End'); |
|
} |
|
|
|
|
|
|
|
document.getElementById('chatForm').addEventListener('submit', function(event) { |
|
event.preventDefault(); // Prevent form submission |
|
|
|
// Get user message from input field |
|
const userMessage = document.getElementById('userMessage').value; |
|
|
|
// Package message in JSON format |
|
const messageJSON = JSON.stringify({ "message_from_user": userMessage }); |
|
|
|
// Send JSON to the server |
|
fetch('http://localhost:5000/get_video_from_text', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: messageJSON |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
// Extract video URL from server response |
|
const videoUrl = data.video_url; |
|
|
|
// Change the source of the placeholder video |
|
const videoPlayer = document.getElementById('videoPlayer'); |
|
videoPlayer.muted = false |
|
videoPlayer.hidden = false |
|
videoPlayer.onended = function(){videoPlayer.hidden = true;} |
|
videoPlayer.setAttribute('src', videoUrl); |
|
}) |
|
.catch(error => console.error('Error:', error)); |
|
}); |
|
</script> |
|
|
|
|
|
</body><div></div></html> |
|
''' |
|
return client_html |
|
|
|
|
|
|
|
@app.route('/synthetic_patient_demo', methods=['GET']) |
|
def demo(message_from_user=None): |
|
client_html = ''' |
|
<html lang="en"><head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Video Chat</title> |
|
|
|
|
|
|
|
<style> |
|
|
|
#submit_chat:hover {background-color: #3c3c3c;} |
|
|
|
#submit_chat { |
|
background-color: black; |
|
} |
|
|
|
#submit_chat:active { |
|
background-color: #2f8383; |
|
} |
|
</style> |
|
|
|
</head> |
|
<body style=" |
|
margin: 0px; |
|
"> |
|
<div id="holder" style="width: 100%; max-width: 600px; margin: 0 auto;"> |
|
<div> |
|
<video id="videoPlayer" width="100%" class="active-video sp-video" src="" style="width: 100%;" autoplay muted hidden></video> |
|
<video id="idleVideoPlayer" class="idle-video sp-video" src="https://new-fond-dog.ngrok-free.app/get_video?filename=idle_high-res_720_26s_adapter.webm" autoplay="" loop="" muted="" style=" |
|
width: 100%; |
|
"></video> |
|
</div> |
|
<div style=" |
|
"> |
|
<form id="chatForm" style=" |
|
display: flex; |
|
flex-direction: column; |
|
"> |
|
|
|
<textarea type="textarea" id="userMessage" name="userMessage" style=" |
|
border: 1px solid black; |
|
background: rgba(239, 239, 239, 1); |
|
min-height: 100px; |
|
font-family: 'IBM Plex Mono', monospace; |
|
padding: 10px; |
|
resize: none; |
|
"> Enter your message here. </textarea> |
|
|
|
<div id="loading_overlay" name="loading_overlay" style=" |
|
min-height: 152px; |
|
font-family: 'IBM Plex Mono', monospace; |
|
position: absolute; |
|
width: 100%; |
|
display: none; |
|
background: rgba(220, 220, 220, 0.8); |
|
"> Loading... </div> |
|
|
|
|
|
<input type="submit" id="submit_chat" value="Submit" style=" |
|
min-width: 119px; |
|
color: white; |
|
font-family: 'IBM Plex Mono',monospace; |
|
padding: 15px; |
|
"> |
|
</form> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
function sleep(ms) { |
|
return new Promise(resolve => setTimeout(resolve, ms)); |
|
} |
|
|
|
async function example(ms) { |
|
console.log('Start'); |
|
await sleep(ms); |
|
console.log('End'); |
|
} |
|
|
|
|
|
|
|
document.getElementById('chatForm').addEventListener('submit', function(event) { |
|
event.preventDefault(); // Prevent form submission |
|
|
|
// Get user message from input field |
|
const userMessage = document.getElementById('userMessage').value; |
|
|
|
// Package message in JSON format |
|
const messageJSON = JSON.stringify({ "message_from_user": userMessage }); |
|
|
|
// Send JSON to the server |
|
fetch('https://new-fond-dog.ngrok-free.app/get_video_from_text', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: messageJSON |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
// Extract video URL from server response |
|
const videoUrl = data.video_url; |
|
|
|
// Change the source of the placeholder video |
|
const videoPlayer = document.getElementById('videoPlayer'); |
|
const idleVideoPlayer = document.getElementById('idleVideoPlayer'); |
|
|
|
videoPlayer.muted = false |
|
videoPlayer.hidden = false |
|
videoPlayer.onended = function(){ |
|
videoPlayer.hidden = true; |
|
idleVideoPlayer.hidden = false; |
|
idleVideoPlayer.play(); |
|
} |
|
videoPlayer.setAttribute('src', videoUrl); idleVideoPlayer.hidden = true; }) .catch(error => |
|
console.error('Error:', error)); }); </script> |
|
|
|
|
|
</body><div></div></html> |
|
''' |
|
return client_html |
|
|
|
|
|
processor = Processor() |
|
request_patient_generation() |
|
|
|
if __name__ == '__main__': |
|
app.run(host="0.0.0.0", debug=False, threaded=False) |
|
|