lipsync-docker / app.py
naicoi's picture
fix-oom (#3)
2f3f0c4
"""
OutofLipSync - Lipsync Only Application
Main Gradio UI module
"""
import os
# Optimize PyTorch memory allocation to reduce fragmentation
os.environ["PYTORCH_ALLOC_CONF"] = "expandable_segments:True"
import logging
import sys
import shutil
import gradio as gr
import torchvision.transforms.functional as _F
from processing import lipsync_with_audio_target
from shared.model_manager import ModelManager
logging.info("=" * 60)
logging.info("APPLICATION STARTING")
logging.info(f"Python version: {sys.version}")
logging.info(f"Platform: {sys.platform}")
logging.info(f"Working directory: {os.getcwd()}")
logging.info("=" * 60)
sys.modules["torchvision.transforms.functional_tensor"] = _F
os.environ["PROCESSED_RESULTS"] = os.path.join(os.getcwd(), "processed_results")
os.makedirs(os.environ["PROCESSED_RESULTS"], exist_ok=True)
src = "/models"
dst = os.path.expanduser("~/.cache/torch/hub/checkpoints")
os.makedirs(dst, exist_ok=True)
if os.path.exists(src):
for item in os.listdir(src):
src_path = os.path.join(src, item)
dst_path = os.path.join(dst, item)
if os.path.isfile(src_path) and not os.path.exists(dst_path):
shutil.copy2(src_path, dst_path)
print(f"Copied {item} to {dst}")
print("Done copying checkpoints!")
print("Loading LatentSync models...")
manager = ModelManager.get_instance()
manager.preload_latentsync_models()
print("Models loaded!")
css = """
#col-container {
margin: 0 auto;
max-width: 1400px;
padding: 2rem 1rem;
}
.header-container {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 1rem;
padding: 2rem;
margin-bottom: 1.5rem;
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
}
.header-title {
color: white;
margin: 0;
font-size: 2.5rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.header-subtitle {
color: rgba(255, 255, 255, 0.9);
margin: 0.5rem 0 0;
font-size: 1.1rem;
}
.card-section {
background: white;
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
border: 1px solid #e5e7eb;
height: 100%;
transition: all 0.3s ease;
}
.card-section:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
}
.section-header {
color: #1f2937;
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.footer-container {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px solid #e5e7eb;
text-align: center;
color: #6b7280;
font-size: 0.9rem;
}
.footer-link {
color: #667eea;
text-decoration: none;
transition: color 0.2s ease;
}
.footer-link:hover {
color: #764ba2;
}
"""
def cleanup(request: gr.Request):
sid = request.session_hash
if sid:
print(f"{sid} left")
d1 = os.path.join(os.environ["PROCESSED_RESULTS"], sid)
shutil.rmtree(d1, ignore_errors=True)
def start_session(request: gr.Request):
return request.session_hash
with gr.Blocks(css=css) as demo:
session_state = gr.State()
demo.load(fn=start_session, outputs=[session_state])
with gr.Column(elem_id="col-container"):
gr.HTML(
"""
<div class="header-container">
<h1 class="header-title">🎬 OutofLipSync</h1>
<p class="header-subtitle">Lipsync video with custom audio (English only)</p>
</div>
"""
)
with gr.Row():
with gr.Column(scale=1):
with gr.Group(elem_classes="card-section"):
gr.HTML('<div class="section-header">πŸ“Ή Upload Video</div>')
video_input = gr.Video(label="Video Source", height=400)
with gr.Column(scale=1):
with gr.Group(elem_classes="card-section"):
gr.HTML('<div class="section-header">🎡 Upload Audio</div>')
audio_input = gr.Audio(
label="Target Audio (English only)", type="filepath"
)
quality_level = gr.Radio(
choices=["Fast", "Normal", "Medium", "Best", "Super Best"],
value="Normal",
label="Quality",
)
lipsync_only_btn = gr.Button(
"πŸ‘„ Lipsync", variant="primary", size="lg"
)
with gr.Group(elem_classes="card-section"):
gr.HTML('<div class="section-header">🎬 Final Output</div>')
final_video = gr.Video(label="Final Output", height=500)
gr.HTML(
"""
<div class="footer-container">
<p>Made with β™₯ by <a href="#" class="footer-link">LT Tech</a> β€’ Powered by <a href="#" class="footer-link">LatentSync</a></p>
<p style="margin-top: 0.5rem; font-size: 0.85rem;">Version 1.0.0</p>
</div>
"""
)
lipsync_only_btn.click(
fn=lipsync_with_audio_target,
inputs=[video_input, audio_input, session_state, quality_level],
outputs=[final_video],
)
if __name__ == "__main__":
demo.unload(cleanup)
demo.queue()
demo.launch(ssr_mode=False, share=True)