File size: 5,990 Bytes
515adca
 
70f271a
515adca
 
 
2f6382c
515adca
 
569bdec
515adca
569bdec
 
70f271a
569bdec
515adca
 
569bdec
515adca
569bdec
70f271a
4495cd2
 
70f271a
515adca
2f6382c
 
70f271a
2f6382c
515adca
70f271a
 
569bdec
 
515adca
569bdec
70f271a
 
 
515adca
 
569bdec
515adca
 
 
569bdec
70f271a
569bdec
515adca
2f6382c
70f271a
569bdec
515adca
 
 
 
e0dc7aa
515adca
70f271a
569bdec
515adca
 
569bdec
 
 
 
515adca
 
e0dc7aa
 
 
 
 
 
 
 
3bc3298
 
 
e0dc7aa
 
 
 
 
 
 
 
 
 
 
 
569bdec
e0dc7aa
 
 
 
 
 
 
 
569bdec
515adca
2f6382c
569bdec
515adca
e0dc7aa
569bdec
 
70f271a
515adca
 
 
2f6382c
515adca
5159f05
 
515adca
569bdec
2f6382c
569bdec
 
2f6382c
 
515adca
 
2f6382c
569bdec
5159f05
2f6382c
5159f05
2f6382c
 
 
5159f05
 
 
2f6382c
5159f05
569bdec
70f271a
569bdec
2f6382c
 
 
 
e0dc7aa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""
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
    )