|
|
import os |
|
|
import sys |
|
|
import types |
|
|
import uuid |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
os.environ["OMP_NUM_THREADS"] = "1" |
|
|
|
|
|
|
|
|
google = types.ModuleType("google") |
|
|
colab = types.ModuleType("google.colab") |
|
|
|
|
|
class _DummyDrive: |
|
|
def mount(self, *args, **kwargs): |
|
|
print("google.colab.drive.mount() called (dummy).") |
|
|
|
|
|
colab.drive = _DummyDrive() |
|
|
google.colab = colab |
|
|
|
|
|
sys.modules["google"] = google |
|
|
sys.modules["google.colab"] = colab |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import gradio as gr |
|
|
from wyn_wav2lip.wav2lip import Wav2Lip |
|
|
import soundfile as sf |
|
|
|
|
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
|
MEDIA_DIR = os.path.join(BASE_DIR, "media") |
|
|
os.makedirs(MEDIA_DIR, exist_ok=True) |
|
|
|
|
|
print("Initialising Wav2Lip...") |
|
|
wav2lip = Wav2Lip() |
|
|
wav2lip.setup() |
|
|
print("Wav2Lip ready.") |
|
|
|
|
|
|
|
|
def run_wav2lip(image_path: str, audio_path: str) -> str: |
|
|
"""Run Wav2Lip and return absolute path to created video file.""" |
|
|
existing_mp4 = { |
|
|
f for f in os.listdir(MEDIA_DIR) |
|
|
if f.lower().endswith(".mp4") |
|
|
} |
|
|
|
|
|
old_cwd = os.getcwd() |
|
|
os.chdir(MEDIA_DIR) |
|
|
try: |
|
|
|
|
|
wav2lip.run( |
|
|
video_file=os.path.basename(image_path), |
|
|
vocal_file=os.path.basename(audio_path), |
|
|
) |
|
|
finally: |
|
|
os.chdir(old_cwd) |
|
|
|
|
|
new_mp4 = [ |
|
|
f for f in os.listdir(MEDIA_DIR) |
|
|
if f.lower().endswith(".mp4") and f not in existing_mp4 |
|
|
] |
|
|
if not new_mp4: |
|
|
mp4_candidates = [ |
|
|
os.path.join(MEDIA_DIR, f) |
|
|
for f in os.listdir(MEDIA_DIR) |
|
|
if f.lower().endswith(".mp4") |
|
|
] |
|
|
if not mp4_candidates: |
|
|
raise RuntimeError("No MP4 created by Wav2Lip.") |
|
|
return max(mp4_candidates, key=os.path.getmtime) |
|
|
|
|
|
return os.path.join(MEDIA_DIR, new_mp4[0]) |
|
|
|
|
|
|
|
|
def lipsync_func(image, audio): |
|
|
""" |
|
|
image: PIL image (from gr.Image) |
|
|
audio: (sr, data) tuple (from gr.Audio(type="numpy")) |
|
|
""" |
|
|
if image is None or audio is None: |
|
|
return None |
|
|
|
|
|
|
|
|
img_id = uuid.uuid4().hex |
|
|
image_path = os.path.join(MEDIA_DIR, f"{img_id}.png") |
|
|
image.save(image_path) |
|
|
|
|
|
|
|
|
aud_id = uuid.uuid4().hex |
|
|
audio_path = os.path.join(MEDIA_DIR, f"{aud_id}.wav") |
|
|
sr, data = audio |
|
|
sf.write(audio_path, data, sr) |
|
|
|
|
|
|
|
|
video_path = run_wav2lip(image_path, audio_path) |
|
|
return video_path |
|
|
|
|
|
|
|
|
demo = gr.Interface( |
|
|
fn=lipsync_func, |
|
|
inputs=[ |
|
|
gr.Image(type="pil", label="Teacher image"), |
|
|
gr.Audio(type="numpy", label="Teacher audio (.wav)") |
|
|
], |
|
|
outputs=gr.Video(label="Lip-synced video"), |
|
|
title="Wav2Lip Lipsync Service", |
|
|
description="Upload a static teacher image and a WAV audio. The model will generate a talking video." |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |
|
|
|