ginipick's picture
Update app.py
3bc3298 verified
"""
Gradio UI that calls the remote animation server.
ANIM_API_URL ν™˜κ²½λ³€μˆ˜λ₯Ό μ„€μ •ν•΄ μ„œλ²„ μ£Όμ†Œλ₯Ό μ£Όμž…ν•˜μ„Έμš”.
ANIM_API_URL=http://211.233.58.201:7788/
"""
import os, logging
from datetime import datetime
import gradio as gr
import httpx
from gradio_client import Client, handle_file
# ──────────────────────── λ‘œκΉ… ────────────────────────── #
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
log = logging.getLogger(__name__)
# ──────────────── μ„œλ²„ URL κ²°μ • ─────────────────────── #
DEFAULT_URL = "http://127.0.0.1:7862/"
REMOTE_URL = "http://211.233.58.201:7862/"
API_URL = os.getenv("ANIM_API_URL", REMOTE_URL)
if "127.0.0.1" in API_URL and os.getenv("HF_SPACE") == "true":
raise RuntimeError(
"HF Space μ»¨ν…Œμ΄λ„ˆ μ•ˆμ—μ„œλŠ” 곡인 IPλ‚˜ 도메인을 ANIM_API_URL 둜 μ§€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€."
)
# ──────────────── ν—¬μŠ€μ²΄ν¬ ν•¨μˆ˜ ─────────────────────── #
TIMEOUT = httpx.Timeout(connect=30.0, read=120.0, write=120.0, pool=30.0)
def test_api_connection():
now = datetime.now().strftime("%H:%M:%S")
try:
resp = httpx.get(f"{API_URL.rstrip('/')}/healthz", timeout=TIMEOUT)
ready = resp.json().get("ready", False)
msg = f"[{now}] μ„œλ²„ μ—°κ²° 성곡 βœ… (ready={ready})"
log.info(msg)
return True, msg
except Exception as e:
msg = f"[{now}] μ„œλ²„ μ—°κ²° μ‹€νŒ¨ ❌ : {e}"
log.error(msg)
return False, msg
# ──────────────── μ• λ‹ˆλ©”μ΄μ…˜ 생성 ───────────────────── #
def generate_animation(image, audio, guidance_scale, steps, progress=gr.Progress()):
start = datetime.now().strftime("%H:%M:%S")
logs = [f"[{start}] μš”μ²­ μ‹œμž‘"]
try:
if image is None or audio is None:
raise ValueError("이미지와 μ˜€λ””μ˜€λ₯Ό λͺ¨λ‘ μ—…λ‘œλ“œν•˜μ„Έμš”.")
progress(0.05, desc="파일 μ€€λΉ„")
client = Client(API_URL)
progress(0.15, desc="μ„œλ²„ 호좜 쀑… (수 λΆ„ μ†Œμš” κ°€λŠ₯)")
result = client.predict(
image_path=handle_file(image),
audio_path=handle_file(audio),
guidance_scale=guidance_scale,
steps=steps,
api_name="/generate_animation"
)
progress(0.95, desc="κ²°κ³Ό 정리")
# κ²°κ³Ό 처리 - dict ν˜•νƒœ 처리 μΆ”κ°€
def extract_video_path(obj):
"""λΉ„λ””μ˜€ κ°μ²΄μ—μ„œ 경둜 μΆ”μΆœ"""
if isinstance(obj, str):
return obj
elif isinstance(obj, dict):
# Gradio의 FileData dict 처리
if 'video' in obj:
return obj['video'] # {'video': '경둜', 'subtitles': None} ν˜•νƒœ 처리
elif 'path' in obj:
return obj['path']
elif 'url' in obj:
return obj['url']
elif 'name' in obj:
return obj['name']
else:
log.warning(f"Unexpected dict structure: {obj.keys()}")
return None
else:
log.warning(f"Unexpected type: {type(obj)}")
return None
if isinstance(result, (list, tuple)) and len(result) >= 2:
anim_path = extract_video_path(result[0])
comp_path = extract_video_path(result[1])
if anim_path and comp_path:
logs.append(f"[{datetime.now().strftime('%H:%M:%S')}] 성곡")
return anim_path, comp_path, "\n".join(logs)
else:
raise RuntimeError(f"λΉ„λ””μ˜€ 경둜 μΆ”μΆœ μ‹€νŒ¨: {result}")
else:
raise RuntimeError(f"μ˜ˆμƒμΉ˜ λͺ»ν•œ λ°˜ν™˜ ν˜•μ‹: {type(result)}")
except Exception as e:
logs.append(f"[{datetime.now().strftime('%H:%M:%S')}] 였λ₯˜: {e}")
log.error(f"Animation generation error: {e}", exc_info=True)
return None, None, "\n".join(logs)
# ─────────────────────── UI μ •μ˜ ────────────────────── #
with gr.Blocks(title="Animation Generator Client") as demo:
gr.Markdown("# 🎬 Animation Generator – Client UI")
# μ„œλ²„ μƒνƒœ 체크
status_box = gr.Textbox(label="API μƒνƒœ", interactive=False)
test_btn = gr.Button("μ„œλ²„ μ—°κ²° ν…ŒμŠ€νŠΈ")
test_btn.click(test_api_connection, outputs=[status_box, status_box])
gr.Markdown("---")
with gr.Row():
with gr.Column():
img_in = gr.Image(type="filepath", label="Portrait Image")
aud_in = gr.Audio(type="filepath", label="Driving Audio")
scale = gr.Slider(1, 10, value=3.0, step=0.1, label="Guidance Scale")
steps = gr.Slider(5, 30, value=10, step=1, label="Inference Steps")
gen_btn = gr.Button("πŸš€ Generate")
with gr.Column():
anim_out = gr.Video(label="Animation Result")
comp_out = gr.Video(label="Side-by-Side")
with gr.Accordion("μ‹€ν–‰ 둜그", open=False):
log_out = gr.Textbox(label="Logs", lines=12, max_lines=20, interactive=False)
gen_btn.click(
generate_animation,
inputs=[img_in, aud_in, scale, steps],
outputs=[anim_out, comp_out, log_out]
)
# ─────────────────────── μ‹€ν–‰ ───────────────────────── #
if __name__ == "__main__":
demo.queue(max_size=4).launch(
server_name="0.0.0.0",
server_port=7860,
show_api=False
)