Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,315 +1,716 @@
|
|
| 1 |
-
#์ต๋ 7720ํ๋ ์ = 321.6์ด x 24fps
|
| 2 |
-
|
| 3 |
-
import os
|
| 4 |
-
import spaces
|
| 5 |
-
import torch
|
| 6 |
-
from diffusers.pipelines.wan.pipeline_wan_i2v import WanImageToVideoPipeline
|
| 7 |
-
from diffusers.models.transformers.transformer_wan import WanTransformer3DModel
|
| 8 |
-
from diffusers.utils.export_utils import export_to_video
|
| 9 |
import gradio as gr
|
| 10 |
-
import tempfile
|
| 11 |
import numpy as np
|
| 12 |
-
from PIL import Image
|
| 13 |
import random
|
| 14 |
-
import
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
from torchao.quantization import Float8DynamicActivationFloat8WeightConfig, Int8WeightOnlyConfig
|
| 18 |
-
import aoti
|
| 19 |
-
|
| 20 |
-
# =========================================================
|
| 21 |
-
# MODEL CONFIGURATION
|
| 22 |
-
# =========================================================
|
| 23 |
-
MODEL_ID = os.getenv("MODEL_ID")
|
| 24 |
-
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 25 |
-
|
| 26 |
-
MAX_DIM = 832
|
| 27 |
-
MIN_DIM = 480
|
| 28 |
-
SQUARE_DIM = 640
|
| 29 |
-
MULTIPLE_OF = 16
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
|
| 37 |
-
MIN_DURATION = 0.5
|
| 38 |
-
MAX_DURATION = 10.0
|
| 39 |
|
| 40 |
# =========================================================
|
| 41 |
-
#
|
| 42 |
# =========================================================
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
transformer=WanTransformer3DModel.from_pretrained(
|
| 47 |
-
MODEL_ID,
|
| 48 |
-
subfolder="transformer",
|
| 49 |
-
torch_dtype=torch.bfloat16,
|
| 50 |
-
device_map="cuda",
|
| 51 |
-
token=HF_TOKEN
|
| 52 |
-
),
|
| 53 |
-
transformer_2=WanTransformer3DModel.from_pretrained(
|
| 54 |
-
MODEL_ID,
|
| 55 |
-
subfolder="transformer_2",
|
| 56 |
-
torch_dtype=torch.bfloat16,
|
| 57 |
-
device_map="cuda",
|
| 58 |
-
token=HF_TOKEN
|
| 59 |
-
),
|
| 60 |
-
torch_dtype=torch.bfloat16,
|
| 61 |
-
).to("cuda")
|
| 62 |
|
| 63 |
# =========================================================
|
| 64 |
-
#
|
| 65 |
# =========================================================
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
"Kijai/WanVideo_comfy",
|
| 69 |
-
weight_name="Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank128_bf16.safetensors",
|
| 70 |
-
adapter_name="lightx2v"
|
| 71 |
-
)
|
| 72 |
-
pipe.load_lora_weights(
|
| 73 |
-
"Kijai/WanVideo_comfy",
|
| 74 |
-
weight_name="Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank128_bf16.safetensors",
|
| 75 |
-
adapter_name="lightx2v_2",
|
| 76 |
-
load_into_transformer_2=True
|
| 77 |
-
)
|
| 78 |
-
|
| 79 |
-
pipe.set_adapters(["lightx2v", "lightx2v_2"], adapter_weights=[1., 1.])
|
| 80 |
-
pipe.fuse_lora(adapter_names=["lightx2v"], lora_scale=3., components=["transformer"])
|
| 81 |
-
pipe.fuse_lora(adapter_names=["lightx2v_2"], lora_scale=1., components=["transformer_2"])
|
| 82 |
-
pipe.unload_lora_weights()
|
| 83 |
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
quantize_(pipe.transformer_2, Float8DynamicActivationFloat8WeightConfig())
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
|
|
|
| 95 |
|
| 96 |
# =========================================================
|
| 97 |
# DEFAULT PROMPTS
|
| 98 |
# =========================================================
|
| 99 |
-
|
| 100 |
default_negative_prompt = "low quality, worst quality, blurry, distorted, deformed, ugly, bad anatomy"
|
| 101 |
|
| 102 |
# =========================================================
|
| 103 |
-
#
|
| 104 |
# =========================================================
|
| 105 |
-
|
| 106 |
-
width, height = image.size
|
| 107 |
-
if width == height:
|
| 108 |
-
return image.resize((SQUARE_DIM, SQUARE_DIM), Image.LANCZOS)
|
| 109 |
-
|
| 110 |
-
aspect_ratio = width / height
|
| 111 |
-
MAX_ASPECT_RATIO = MAX_DIM / MIN_DIM
|
| 112 |
-
MIN_ASPECT_RATIO = MIN_DIM / MAX_DIM
|
| 113 |
-
|
| 114 |
-
image_to_resize = image
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
image_to_resize = image.crop((0, top, width, top + crop_height))
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
target_h = MAX_DIM
|
| 130 |
-
target_w = int(round(target_h * aspect_ratio))
|
| 131 |
|
| 132 |
-
final_w = round(target_w / MULTIPLE_OF) * MULTIPLE_OF
|
| 133 |
-
final_h = round(target_h / MULTIPLE_OF) * MULTIPLE_OF
|
| 134 |
|
| 135 |
-
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
-
return image_to_resize.resize((final_w, final_h), Image.LANCZOS)
|
| 139 |
|
| 140 |
# =========================================================
|
| 141 |
-
#
|
| 142 |
# =========================================================
|
| 143 |
-
def get_num_frames(duration_seconds: float):
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
input_image, prompt, steps, negative_prompt,
|
| 148 |
-
duration_seconds, guidance_scale, guidance_scale_2,
|
| 149 |
-
seed, randomize_seed, progress,
|
| 150 |
-
):
|
| 151 |
-
if input_image is None:
|
| 152 |
-
return 120
|
| 153 |
-
|
| 154 |
-
BASE_FRAMES_HEIGHT_WIDTH = 81 * 832 * 624
|
| 155 |
-
BASE_STEP_DURATION = 15
|
| 156 |
-
width, height = resize_image(input_image).size
|
| 157 |
-
frames = get_num_frames(duration_seconds)
|
| 158 |
-
factor = frames * width * height / BASE_FRAMES_HEIGHT_WIDTH
|
| 159 |
-
step_duration = BASE_STEP_DURATION * factor ** 1.5
|
| 160 |
-
return 10 + int(steps) * step_duration
|
| 161 |
|
| 162 |
# =========================================================
|
| 163 |
# MAIN GENERATION FUNCTION
|
| 164 |
# =========================================================
|
| 165 |
-
@spaces.GPU
|
| 166 |
def generate_video(
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
|
|
|
| 176 |
progress=gr.Progress(track_tqdm=True),
|
| 177 |
-
):
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
|
|
|
|
|
|
|
|
|
| 188 |
negative_prompt=negative_prompt,
|
| 189 |
-
height=
|
| 190 |
-
width=
|
| 191 |
num_frames=num_frames,
|
| 192 |
-
guidance_scale=
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
generator=torch.Generator(device="cuda").manual_seed(current_seed),
|
| 196 |
).frames[0]
|
| 197 |
-
|
|
|
|
| 198 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 199 |
video_path = tmpfile.name
|
| 200 |
-
export_to_video(
|
| 201 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
|
| 203 |
# =========================================================
|
| 204 |
-
# GRADIO UI
|
| 205 |
# =========================================================
|
| 206 |
with gr.Blocks() as demo:
|
| 207 |
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
<p
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
with gr.Row():
|
| 230 |
-
with gr.Column(scale=1):
|
| 231 |
-
input_image_component = gr.Image(
|
| 232 |
-
type="pil",
|
| 233 |
-
label="๐ท Upload Image",
|
| 234 |
-
height=350
|
| 235 |
-
)
|
| 236 |
prompt_input = gr.Textbox(
|
| 237 |
-
label="โ๏ธ Prompt",
|
| 238 |
-
value=
|
| 239 |
-
placeholder="Describe the
|
| 240 |
-
lines=
|
| 241 |
)
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
label="โฑ๏ธ Duration (seconds)"
|
| 248 |
)
|
| 249 |
-
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
negative_prompt_input = gr.Textbox(
|
| 252 |
-
label="Negative Prompt",
|
| 253 |
-
value=default_negative_prompt,
|
| 254 |
lines=2
|
| 255 |
)
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
)
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
)
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
)
|
| 277 |
-
|
| 278 |
-
label="
|
| 279 |
-
minimum=
|
| 280 |
-
maximum=
|
| 281 |
-
step=
|
| 282 |
-
value=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
)
|
| 284 |
randomize_seed_checkbox = gr.Checkbox(
|
| 285 |
-
label="Randomize Seed",
|
| 286 |
value=True
|
| 287 |
)
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
video_output = gr.Video(
|
| 296 |
-
label="๐ฅ Generated Video",
|
| 297 |
autoplay=True,
|
| 298 |
-
height=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
)
|
| 300 |
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
]
|
| 307 |
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
)
|
| 313 |
|
|
|
|
|
|
|
| 314 |
if __name__ == "__main__":
|
| 315 |
demo.queue().launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
| 2 |
import numpy as np
|
|
|
|
| 3 |
import random
|
| 4 |
+
import torch
|
| 5 |
+
import spaces
|
| 6 |
+
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
from PIL import Image
|
| 9 |
+
from diffusers.pipelines.wan.pipeline_wan import WanPipeline
|
| 10 |
+
from diffusers.utils.export_utils import export_to_video
|
| 11 |
|
| 12 |
+
import tempfile
|
| 13 |
+
from typing import Optional, Tuple, Any
|
| 14 |
+
from groq import Groq
|
| 15 |
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# =========================================================
|
| 18 |
+
# API CONFIGURATION
|
| 19 |
# =========================================================
|
| 20 |
+
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
| 21 |
+
MODEL_ID = os.getenv("MODEL_ID")
|
| 22 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
# =========================================================
|
| 25 |
+
# MODEL LOADING
|
| 26 |
# =========================================================
|
| 27 |
+
dtype = torch.bfloat16
|
| 28 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
+
print("Loading pipeline...")
|
| 31 |
+
pipe = WanPipeline.from_pretrained(
|
| 32 |
+
MODEL_ID,
|
| 33 |
+
torch_dtype=dtype,
|
| 34 |
+
token=HF_TOKEN
|
| 35 |
+
).to(device)
|
|
|
|
| 36 |
|
| 37 |
+
MAX_SEED = np.iinfo(np.int32).max
|
| 38 |
+
FIXED_FPS = 24
|
| 39 |
+
MIN_FRAMES = 8
|
| 40 |
+
MAX_FRAMES = 81
|
| 41 |
|
| 42 |
# =========================================================
|
| 43 |
# DEFAULT PROMPTS
|
| 44 |
# =========================================================
|
| 45 |
+
default_prompt = "A beautiful sunset over the ocean with gentle waves."
|
| 46 |
default_negative_prompt = "low quality, worst quality, blurry, distorted, deformed, ugly, bad anatomy"
|
| 47 |
|
| 48 |
# =========================================================
|
| 49 |
+
# PROMPT ENHANCEMENT SYSTEM PROMPT
|
| 50 |
# =========================================================
|
| 51 |
+
ENHANCE_SYSTEM_PROMPT = """You are a professional video prompt engineer. Your task is to enhance user prompts for AI video generation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
Rules:
|
| 54 |
+
1. Add vivid visual details (lighting, colors, textures, atmosphere)
|
| 55 |
+
2. Include camera movements (pan, zoom, tracking shot, etc.)
|
| 56 |
+
3. Describe motion and dynamics clearly
|
| 57 |
+
4. Keep the enhanced prompt concise but detailed (max 150 words)
|
| 58 |
+
5. Maintain the original intent of the user's prompt
|
| 59 |
+
6. Output ONLY the enhanced prompt, nothing else
|
|
|
|
| 60 |
|
| 61 |
+
Example:
|
| 62 |
+
User: "A cat playing"
|
| 63 |
+
Enhanced: "A fluffy orange tabby cat playfully batting at a dangling yarn ball, soft afternoon sunlight streaming through a window creating warm golden highlights on its fur, smooth tracking shot following the cat's graceful movements, shallow depth of field with bokeh background, cozy living room setting with warm ambient lighting"
|
| 64 |
+
"""
|
|
|
|
|
|
|
| 65 |
|
|
|
|
|
|
|
| 66 |
|
| 67 |
+
# =========================================================
|
| 68 |
+
# PROMPT ENHANCEMENT FUNCTION
|
| 69 |
+
# =========================================================
|
| 70 |
+
def enhance_prompt(prompt: str) -> str:
|
| 71 |
+
"""Enhance the user prompt using Groq LLM API."""
|
| 72 |
+
if not GROQ_API_KEY:
|
| 73 |
+
return prompt + " (API key not configured - using original prompt)"
|
| 74 |
+
|
| 75 |
+
try:
|
| 76 |
+
client = Groq(api_key=GROQ_API_KEY)
|
| 77 |
+
|
| 78 |
+
enhanced_text = ""
|
| 79 |
+
completion = client.chat.completions.create(
|
| 80 |
+
model="meta-llama/llama-4-scout-17b-16e-instruct",
|
| 81 |
+
messages=[
|
| 82 |
+
{
|
| 83 |
+
"role": "system",
|
| 84 |
+
"content": ENHANCE_SYSTEM_PROMPT
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"role": "user",
|
| 88 |
+
"content": f"Enhance this video generation prompt: {prompt}"
|
| 89 |
+
}
|
| 90 |
+
],
|
| 91 |
+
temperature=0.7,
|
| 92 |
+
max_completion_tokens=512,
|
| 93 |
+
top_p=1,
|
| 94 |
+
stream=True,
|
| 95 |
+
stop=None
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
for chunk in completion:
|
| 99 |
+
if chunk.choices[0].delta.content:
|
| 100 |
+
enhanced_text += chunk.choices[0].delta.content
|
| 101 |
+
|
| 102 |
+
return enhanced_text.strip() if enhanced_text.strip() else prompt
|
| 103 |
+
|
| 104 |
+
except Exception as e:
|
| 105 |
+
print(f"Prompt enhancement error: {e}")
|
| 106 |
+
return prompt
|
| 107 |
|
|
|
|
| 108 |
|
| 109 |
# =========================================================
|
| 110 |
+
# HELPER FUNCTIONS
|
| 111 |
# =========================================================
|
| 112 |
+
def get_num_frames(duration_seconds: float) -> int:
|
| 113 |
+
"""Calculate number of frames based on duration."""
|
| 114 |
+
return max(MIN_FRAMES, min(MAX_FRAMES, int(duration_seconds * FIXED_FPS)))
|
| 115 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
# =========================================================
|
| 118 |
# MAIN GENERATION FUNCTION
|
| 119 |
# =========================================================
|
| 120 |
+
@spaces.GPU
|
| 121 |
def generate_video(
|
| 122 |
+
prompt: str,
|
| 123 |
+
negative_prompt: str = default_negative_prompt,
|
| 124 |
+
enhance_prompt_option: bool = False,
|
| 125 |
+
duration_seconds: float = 3.0,
|
| 126 |
+
guidance_scale: float = 7.5,
|
| 127 |
+
num_inference_steps: int = 20,
|
| 128 |
+
height: int = 480,
|
| 129 |
+
width: int = 832,
|
| 130 |
+
seed: int = 0,
|
| 131 |
+
randomize_seed: bool = True,
|
| 132 |
progress=gr.Progress(track_tqdm=True),
|
| 133 |
+
) -> Tuple[str, int, str]:
|
| 134 |
+
"""Generate video from text prompt."""
|
| 135 |
+
|
| 136 |
+
if not prompt.strip():
|
| 137 |
+
raise gr.Error("Please enter a prompt.")
|
| 138 |
+
|
| 139 |
+
# Enhance prompt if option is enabled
|
| 140 |
+
final_prompt = prompt
|
| 141 |
+
if enhance_prompt_option:
|
| 142 |
+
final_prompt = enhance_prompt(prompt)
|
| 143 |
+
print(f"Enhanced Prompt: {final_prompt}")
|
| 144 |
+
|
| 145 |
+
# Handle seed
|
| 146 |
current_seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
|
| 147 |
+
generator = torch.Generator(device=device).manual_seed(current_seed)
|
| 148 |
+
|
| 149 |
+
# Calculate frames
|
| 150 |
+
num_frames = get_num_frames(duration_seconds)
|
| 151 |
+
|
| 152 |
+
# Generate video
|
| 153 |
+
output_frames = pipe(
|
| 154 |
+
prompt=final_prompt,
|
| 155 |
negative_prompt=negative_prompt,
|
| 156 |
+
height=height,
|
| 157 |
+
width=width,
|
| 158 |
num_frames=num_frames,
|
| 159 |
+
guidance_scale=guidance_scale,
|
| 160 |
+
num_inference_steps=num_inference_steps,
|
| 161 |
+
generator=generator,
|
|
|
|
| 162 |
).frames[0]
|
| 163 |
+
|
| 164 |
+
# Export to video file
|
| 165 |
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmpfile:
|
| 166 |
video_path = tmpfile.name
|
| 167 |
+
export_to_video(output_frames, video_path, fps=FIXED_FPS)
|
| 168 |
+
|
| 169 |
+
# Build info log
|
| 170 |
+
info_log = f"""โ
VIDEO GENERATION COMPLETE!
|
| 171 |
+
{'=' * 50}
|
| 172 |
+
๐ฌ Video Info:
|
| 173 |
+
โข Duration: {duration_seconds:.1f} seconds
|
| 174 |
+
โข Total Frames: {num_frames}
|
| 175 |
+
โข FPS: {FIXED_FPS}
|
| 176 |
+
โข Resolution: {width} x {height}
|
| 177 |
+
{'=' * 50}
|
| 178 |
+
โ๏ธ Generation Settings:
|
| 179 |
+
โข Guidance Scale: {guidance_scale}
|
| 180 |
+
โข Inference Steps: {num_inference_steps}
|
| 181 |
+
โข Seed: {current_seed}
|
| 182 |
+
โข Prompt Enhanced: {'Yes' if enhance_prompt_option else 'No'}
|
| 183 |
+
{'=' * 50}
|
| 184 |
+
๐พ Ready to download!"""
|
| 185 |
+
|
| 186 |
+
return video_path, current_seed, final_prompt, info_log
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
# ============================================
|
| 190 |
+
# ๐จ Comic Classic Theme - Toon Playground
|
| 191 |
+
# ============================================
|
| 192 |
+
|
| 193 |
+
css = """
|
| 194 |
+
/* ===== ๐จ Google Fonts Import ===== */
|
| 195 |
+
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap');
|
| 196 |
+
|
| 197 |
+
/* ===== ๐จ Comic Classic ๋ฐฐ๊ฒฝ - ๋นํฐ์ง ํ์ดํผ + ๋ํธ ํจํด ===== */
|
| 198 |
+
.gradio-container {
|
| 199 |
+
background-color: #FEF9C3 !important;
|
| 200 |
+
background-image:
|
| 201 |
+
radial-gradient(#1F2937 1px, transparent 1px) !important;
|
| 202 |
+
background-size: 20px 20px !important;
|
| 203 |
+
min-height: 100vh !important;
|
| 204 |
+
font-family: 'Comic Neue', cursive, sans-serif !important;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
/* ===== ํ๊น
ํ์ด์ค ์๋จ ์์ ์จ๊น ===== */
|
| 208 |
+
.huggingface-space-header,
|
| 209 |
+
#space-header,
|
| 210 |
+
.space-header,
|
| 211 |
+
[class*="space-header"],
|
| 212 |
+
.svelte-1ed2p3z,
|
| 213 |
+
.space-header-badge,
|
| 214 |
+
.header-badge,
|
| 215 |
+
[data-testid="space-header"],
|
| 216 |
+
.svelte-kqij2n,
|
| 217 |
+
.svelte-1ax1toq,
|
| 218 |
+
.embed-container > div:first-child {
|
| 219 |
+
display: none !important;
|
| 220 |
+
visibility: hidden !important;
|
| 221 |
+
height: 0 !important;
|
| 222 |
+
width: 0 !important;
|
| 223 |
+
overflow: hidden !important;
|
| 224 |
+
opacity: 0 !important;
|
| 225 |
+
pointer-events: none !important;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
/* ===== Footer ์์ ์จ๊น ===== */
|
| 229 |
+
footer,
|
| 230 |
+
.footer,
|
| 231 |
+
.gradio-container footer,
|
| 232 |
+
.built-with,
|
| 233 |
+
[class*="footer"],
|
| 234 |
+
.gradio-footer,
|
| 235 |
+
.main-footer,
|
| 236 |
+
div[class*="footer"],
|
| 237 |
+
.show-api,
|
| 238 |
+
.built-with-gradio,
|
| 239 |
+
a[href*="gradio.app"],
|
| 240 |
+
a[href*="huggingface.co/spaces"] {
|
| 241 |
+
display: none !important;
|
| 242 |
+
visibility: hidden !important;
|
| 243 |
+
height: 0 !important;
|
| 244 |
+
padding: 0 !important;
|
| 245 |
+
margin: 0 !important;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
/* ===== ๋ฉ์ธ ์ปจํ
์ด๋ ===== */
|
| 249 |
+
#col-container {
|
| 250 |
+
max-width: 1000px;
|
| 251 |
+
margin: 0 auto;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
/* ===== ๐จ ํค๋ ํ์ดํ - ์ฝ๋ฏน ์คํ์ผ ===== */
|
| 255 |
+
.header-text h1 {
|
| 256 |
+
font-family: 'Bangers', cursive !important;
|
| 257 |
+
color: #1F2937 !important;
|
| 258 |
+
font-size: 3.5rem !important;
|
| 259 |
+
font-weight: 400 !important;
|
| 260 |
+
text-align: center !important;
|
| 261 |
+
margin-bottom: 0.5rem !important;
|
| 262 |
+
text-shadow:
|
| 263 |
+
4px 4px 0px #FACC15,
|
| 264 |
+
6px 6px 0px #1F2937 !important;
|
| 265 |
+
letter-spacing: 3px !important;
|
| 266 |
+
-webkit-text-stroke: 2px #1F2937 !important;
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
/* ===== ๐จ ์๋ธํ์ดํ ===== */
|
| 270 |
+
.subtitle {
|
| 271 |
+
text-align: center !important;
|
| 272 |
+
font-family: 'Comic Neue', cursive !important;
|
| 273 |
+
font-size: 1.2rem !important;
|
| 274 |
+
color: #1F2937 !important;
|
| 275 |
+
margin-bottom: 1.5rem !important;
|
| 276 |
+
font-weight: 700 !important;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
/* ===== ๐จ ์นด๋/ํจ๋ - ๋งํ ํ๋ ์ ์คํ์ผ ===== */
|
| 280 |
+
.gr-panel,
|
| 281 |
+
.gr-box,
|
| 282 |
+
.gr-form,
|
| 283 |
+
.block,
|
| 284 |
+
.gr-group {
|
| 285 |
+
background: #FFFFFF !important;
|
| 286 |
+
border: 3px solid #1F2937 !important;
|
| 287 |
+
border-radius: 8px !important;
|
| 288 |
+
box-shadow: 6px 6px 0px #1F2937 !important;
|
| 289 |
+
transition: all 0.2s ease !important;
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
.gr-panel:hover,
|
| 293 |
+
.block:hover {
|
| 294 |
+
transform: translate(-2px, -2px) !important;
|
| 295 |
+
box-shadow: 8px 8px 0px #1F2937 !important;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
/* ===== ๐จ ์
๋ ฅ ํ๋ (Textbox) ===== */
|
| 299 |
+
textarea,
|
| 300 |
+
input[type="text"],
|
| 301 |
+
input[type="number"] {
|
| 302 |
+
background: #FFFFFF !important;
|
| 303 |
+
border: 3px solid #1F2937 !important;
|
| 304 |
+
border-radius: 8px !important;
|
| 305 |
+
color: #1F2937 !important;
|
| 306 |
+
font-family: 'Comic Neue', cursive !important;
|
| 307 |
+
font-size: 1rem !important;
|
| 308 |
+
font-weight: 700 !important;
|
| 309 |
+
transition: all 0.2s ease !important;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
textarea:focus,
|
| 313 |
+
input[type="text"]:focus,
|
| 314 |
+
input[type="number"]:focus {
|
| 315 |
+
border-color: #3B82F6 !important;
|
| 316 |
+
box-shadow: 4px 4px 0px #3B82F6 !important;
|
| 317 |
+
outline: none !important;
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
textarea::placeholder {
|
| 321 |
+
color: #9CA3AF !important;
|
| 322 |
+
font-weight: 400 !important;
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
/* ===== ๐จ Primary ๋ฒํผ - ์ฝ๋ฏน ๋ธ๋ฃจ ===== */
|
| 326 |
+
.gr-button-primary,
|
| 327 |
+
button.primary,
|
| 328 |
+
.gr-button.primary {
|
| 329 |
+
background: #3B82F6 !important;
|
| 330 |
+
border: 3px solid #1F2937 !important;
|
| 331 |
+
border-radius: 8px !important;
|
| 332 |
+
color: #FFFFFF !important;
|
| 333 |
+
font-family: 'Bangers', cursive !important;
|
| 334 |
+
font-weight: 400 !important;
|
| 335 |
+
font-size: 1.3rem !important;
|
| 336 |
+
letter-spacing: 2px !important;
|
| 337 |
+
padding: 14px 28px !important;
|
| 338 |
+
box-shadow: 5px 5px 0px #1F2937 !important;
|
| 339 |
+
transition: all 0.1s ease !important;
|
| 340 |
+
text-shadow: 1px 1px 0px #1F2937 !important;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
.gr-button-primary:hover,
|
| 344 |
+
button.primary:hover,
|
| 345 |
+
.gr-button.primary:hover {
|
| 346 |
+
background: #2563EB !important;
|
| 347 |
+
transform: translate(-2px, -2px) !important;
|
| 348 |
+
box-shadow: 7px 7px 0px #1F2937 !important;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
.gr-button-primary:active,
|
| 352 |
+
button.primary:active,
|
| 353 |
+
.gr-button.primary:active {
|
| 354 |
+
transform: translate(3px, 3px) !important;
|
| 355 |
+
box-shadow: 2px 2px 0px #1F2937 !important;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
/* ===== ๐จ Secondary ๋ฒํผ - ์ฝ๋ฏน ๋ ๋ ===== */
|
| 359 |
+
.gr-button-secondary,
|
| 360 |
+
button.secondary,
|
| 361 |
+
.generate-btn {
|
| 362 |
+
background: #EF4444 !important;
|
| 363 |
+
border: 3px solid #1F2937 !important;
|
| 364 |
+
border-radius: 8px !important;
|
| 365 |
+
color: #FFFFFF !important;
|
| 366 |
+
font-family: 'Bangers', cursive !important;
|
| 367 |
+
font-weight: 400 !important;
|
| 368 |
+
font-size: 1.1rem !important;
|
| 369 |
+
letter-spacing: 1px !important;
|
| 370 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 371 |
+
transition: all 0.1s ease !important;
|
| 372 |
+
text-shadow: 1px 1px 0px #1F2937 !important;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
.gr-button-secondary:hover,
|
| 376 |
+
button.secondary:hover,
|
| 377 |
+
.generate-btn:hover {
|
| 378 |
+
background: #DC2626 !important;
|
| 379 |
+
transform: translate(-2px, -2px) !important;
|
| 380 |
+
box-shadow: 6px 6px 0px #1F2937 !important;
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
.gr-button-secondary:active,
|
| 384 |
+
button.secondary:active,
|
| 385 |
+
.generate-btn:active {
|
| 386 |
+
transform: translate(2px, 2px) !important;
|
| 387 |
+
box-shadow: 2px 2px 0px #1F2937 !important;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
/* ===== ๐จ ๋ก๊ทธ ์ถ๋ ฅ ์์ญ ===== */
|
| 391 |
+
.info-log textarea {
|
| 392 |
+
background: #1F2937 !important;
|
| 393 |
+
color: #10B981 !important;
|
| 394 |
+
font-family: 'Courier New', monospace !important;
|
| 395 |
+
font-size: 0.9rem !important;
|
| 396 |
+
font-weight: 400 !important;
|
| 397 |
+
border: 3px solid #10B981 !important;
|
| 398 |
+
border-radius: 8px !important;
|
| 399 |
+
box-shadow: 4px 4px 0px #10B981 !important;
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
/* ===== ๐จ ๋น๋์ค ์ถ๋ ฅ ์์ญ ===== */
|
| 403 |
+
.video-output video {
|
| 404 |
+
border: 4px solid #1F2937 !important;
|
| 405 |
+
border-radius: 8px !important;
|
| 406 |
+
box-shadow: 8px 8px 0px #1F2937 !important;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
/* ===== ๐จ ์์ฝ๋์ธ - ๋งํ์ ์คํ์ผ ===== */
|
| 410 |
+
.gr-accordion {
|
| 411 |
+
background: #FACC15 !important;
|
| 412 |
+
border: 3px solid #1F2937 !important;
|
| 413 |
+
border-radius: 8px !important;
|
| 414 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
.gr-accordion-header {
|
| 418 |
+
color: #1F2937 !important;
|
| 419 |
+
font-family: 'Comic Neue', cursive !important;
|
| 420 |
+
font-weight: 700 !important;
|
| 421 |
+
font-size: 1.1rem !important;
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
/* ===== ๐จ ์ด๋ฏธ์ง ์ถ๋ ฅ ์์ญ ===== */
|
| 425 |
+
.gr-image,
|
| 426 |
+
.image-container {
|
| 427 |
+
border: 4px solid #1F2937 !important;
|
| 428 |
+
border-radius: 8px !important;
|
| 429 |
+
box-shadow: 8px 8px 0px #1F2937 !important;
|
| 430 |
+
overflow: hidden !important;
|
| 431 |
+
background: #FFFFFF !important;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
/* ===== ๐จ ๋ผ๋ฒจ ์คํ์ผ ===== */
|
| 435 |
+
label,
|
| 436 |
+
.gr-input-label,
|
| 437 |
+
.gr-block-label {
|
| 438 |
+
color: #1F2937 !important;
|
| 439 |
+
font-family: 'Comic Neue', cursive !important;
|
| 440 |
+
font-weight: 700 !important;
|
| 441 |
+
font-size: 1rem !important;
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
span.gr-label {
|
| 445 |
+
color: #1F2937 !important;
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
/* ===== ๐จ ์ฒดํฌ๋ฐ์ค ์คํ์ผ ===== */
|
| 449 |
+
input[type="checkbox"] {
|
| 450 |
+
accent-color: #3B82F6 !important;
|
| 451 |
+
width: 20px !important;
|
| 452 |
+
height: 20px !important;
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
/* ===== ๐จ ์ฌ๋ผ์ด๋ ์คํ์ผ ===== */
|
| 456 |
+
input[type="range"] {
|
| 457 |
+
accent-color: #3B82F6 !important;
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
/* ===== ๐จ ์ ๋ณด ํ
์คํธ ===== */
|
| 461 |
+
.gr-info,
|
| 462 |
+
.info {
|
| 463 |
+
color: #6B7280 !important;
|
| 464 |
+
font-family: 'Comic Neue', cursive !important;
|
| 465 |
+
font-size: 0.9rem !important;
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
/* ===== ๐จ ํ๋ก๊ทธ๋ ์ค ๋ฐ ===== */
|
| 469 |
+
.progress-bar,
|
| 470 |
+
.gr-progress-bar {
|
| 471 |
+
background: #3B82F6 !important;
|
| 472 |
+
border: 2px solid #1F2937 !important;
|
| 473 |
+
border-radius: 4px !important;
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
/* ===== ๐จ ์คํฌ๋กค๋ฐ - ์ฝ๋ฏน ์คํ์ผ ===== */
|
| 477 |
+
::-webkit-scrollbar {
|
| 478 |
+
width: 12px;
|
| 479 |
+
height: 12px;
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
::-webkit-scrollbar-track {
|
| 483 |
+
background: #FEF9C3;
|
| 484 |
+
border: 2px solid #1F2937;
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
::-webkit-scrollbar-thumb {
|
| 488 |
+
background: #3B82F6;
|
| 489 |
+
border: 2px solid #1F2937;
|
| 490 |
+
border-radius: 0px;
|
| 491 |
+
}
|
| 492 |
+
|
| 493 |
+
::-webkit-scrollbar-thumb:hover {
|
| 494 |
+
background: #EF4444;
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
/* ===== ๐จ ์ ํ ํ์ด๋ผ์ดํธ ===== */
|
| 498 |
+
::selection {
|
| 499 |
+
background: #FACC15;
|
| 500 |
+
color: #1F2937;
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
/* ===== ๐จ ๋งํฌ ์คํ์ผ ===== */
|
| 504 |
+
a {
|
| 505 |
+
color: #3B82F6 !important;
|
| 506 |
+
text-decoration: none !important;
|
| 507 |
+
font-weight: 700 !important;
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
a:hover {
|
| 511 |
+
color: #EF4444 !important;
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
/* ===== ๐จ Row/Column ๊ฐ๊ฒฉ ===== */
|
| 515 |
+
.gr-row {
|
| 516 |
+
gap: 1.5rem !important;
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
.gr-column {
|
| 520 |
+
gap: 1rem !important;
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
/* ===== ๋ฐ์ํ ์กฐ์ ===== */
|
| 524 |
+
@media (max-width: 768px) {
|
| 525 |
+
.header-text h1 {
|
| 526 |
+
font-size: 2.2rem !important;
|
| 527 |
+
text-shadow:
|
| 528 |
+
3px 3px 0px #FACC15,
|
| 529 |
+
4px 4px 0px #1F2937 !important;
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
+
.gr-button-primary,
|
| 533 |
+
button.primary {
|
| 534 |
+
padding: 12px 20px !important;
|
| 535 |
+
font-size: 1.1rem !important;
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
.gr-panel,
|
| 539 |
+
.block {
|
| 540 |
+
box-shadow: 4px 4px 0px #1F2937 !important;
|
| 541 |
+
}
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
/* ===== ๐จ ๋คํฌ๋ชจ๋ ๋นํ์ฑํ (์ฝ๋ฏน์ ๋ฐ์์ผ ํจ) ===== */
|
| 545 |
+
@media (prefers-color-scheme: dark) {
|
| 546 |
+
.gradio-container {
|
| 547 |
+
background-color: #FEF9C3 !important;
|
| 548 |
+
}
|
| 549 |
+
}
|
| 550 |
+
"""
|
| 551 |
+
|
| 552 |
|
| 553 |
# =========================================================
|
| 554 |
+
# GRADIO UI - Comic Classic Theme
|
| 555 |
# =========================================================
|
| 556 |
with gr.Blocks() as demo:
|
| 557 |
|
| 558 |
+
# CSS ์ฝ์
|
| 559 |
+
gr.HTML(f"<style>{css}</style>")
|
| 560 |
+
|
| 561 |
+
# Header Title
|
| 562 |
+
gr.Markdown(
|
| 563 |
+
"""
|
| 564 |
+
# ๐ฌ UNCENSORED TEXT TO VIDEO ๐ฅ
|
| 565 |
+
""",
|
| 566 |
+
elem_classes="header-text"
|
| 567 |
+
)
|
| 568 |
+
|
| 569 |
+
gr.Markdown(
|
| 570 |
+
"""
|
| 571 |
+
<p class="subtitle">โจ Transform your ideas into stunning AI-generated videos! ๐</p>
|
| 572 |
+
""",
|
| 573 |
+
)
|
| 574 |
+
|
| 575 |
+
with gr.Row(equal_height=False):
|
| 576 |
+
# Left column - Input
|
| 577 |
+
with gr.Column(scale=1, min_width=320):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
prompt_input = gr.Textbox(
|
| 579 |
+
label="โ๏ธ Your Prompt",
|
| 580 |
+
value=default_prompt,
|
| 581 |
+
placeholder="Describe the video you want to create...",
|
| 582 |
+
lines=4
|
| 583 |
)
|
| 584 |
+
|
| 585 |
+
enhance_prompt_checkbox = gr.Checkbox(
|
| 586 |
+
label="โจ Enhance Prompt with AI",
|
| 587 |
+
value=False,
|
| 588 |
+
info="Use AI to automatically enhance your prompt for better results"
|
|
|
|
| 589 |
)
|
| 590 |
+
|
| 591 |
+
duration_slider = gr.Slider(
|
| 592 |
+
label="โฑ๏ธ Duration (seconds)",
|
| 593 |
+
minimum=1.0,
|
| 594 |
+
maximum=4.0,
|
| 595 |
+
step=0.5,
|
| 596 |
+
value=3.0
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
generate_btn = gr.Button(
|
| 600 |
+
"๐ฌ GENERATE VIDEO! ๐",
|
| 601 |
+
variant="primary",
|
| 602 |
+
size="lg",
|
| 603 |
+
elem_classes="generate-btn"
|
| 604 |
+
)
|
| 605 |
+
|
| 606 |
+
with gr.Accordion("โ๏ธ Advanced Options", open=False):
|
| 607 |
negative_prompt_input = gr.Textbox(
|
| 608 |
+
label="Negative Prompt",
|
| 609 |
+
value=default_negative_prompt,
|
| 610 |
lines=2
|
| 611 |
)
|
| 612 |
+
guidance_scale_slider = gr.Slider(
|
| 613 |
+
label="Guidance Scale",
|
| 614 |
+
minimum=1.0,
|
| 615 |
+
maximum=15.0,
|
| 616 |
+
step=0.5,
|
| 617 |
+
value=7.5
|
| 618 |
)
|
| 619 |
+
num_inference_steps_slider = gr.Slider(
|
| 620 |
+
label="Inference Steps",
|
| 621 |
+
minimum=10,
|
| 622 |
+
maximum=50,
|
| 623 |
+
step=1,
|
| 624 |
+
value=20
|
| 625 |
)
|
| 626 |
+
height_slider = gr.Slider(
|
| 627 |
+
label="Height",
|
| 628 |
+
minimum=256,
|
| 629 |
+
maximum=720,
|
| 630 |
+
step=16,
|
| 631 |
+
value=480
|
| 632 |
)
|
| 633 |
+
width_slider = gr.Slider(
|
| 634 |
+
label="Width",
|
| 635 |
+
minimum=256,
|
| 636 |
+
maximum=1280,
|
| 637 |
+
step=16,
|
| 638 |
+
value=832
|
| 639 |
+
)
|
| 640 |
+
seed_slider = gr.Slider(
|
| 641 |
+
label="Seed",
|
| 642 |
+
minimum=0,
|
| 643 |
+
maximum=MAX_SEED,
|
| 644 |
+
step=1,
|
| 645 |
+
value=0
|
| 646 |
)
|
| 647 |
randomize_seed_checkbox = gr.Checkbox(
|
| 648 |
+
label="Randomize Seed",
|
| 649 |
value=True
|
| 650 |
)
|
| 651 |
+
|
| 652 |
+
with gr.Accordion("๐ Generation Info", open=True):
|
| 653 |
+
info_log = gr.Textbox(
|
| 654 |
+
label="",
|
| 655 |
+
placeholder="Generation info will appear here...",
|
| 656 |
+
lines=12,
|
| 657 |
+
max_lines=20,
|
| 658 |
+
interactive=False,
|
| 659 |
+
elem_classes="info-log"
|
| 660 |
+
)
|
| 661 |
+
|
| 662 |
+
# Right column - Output
|
| 663 |
+
with gr.Column(scale=1, min_width=320):
|
| 664 |
video_output = gr.Video(
|
| 665 |
+
label="๐ฅ Generated Video",
|
| 666 |
autoplay=True,
|
| 667 |
+
height=400,
|
| 668 |
+
elem_classes="video-output"
|
| 669 |
+
)
|
| 670 |
+
|
| 671 |
+
final_prompt_output = gr.Textbox(
|
| 672 |
+
label="๐ Final Prompt Used",
|
| 673 |
+
interactive=False,
|
| 674 |
+
lines=3
|
| 675 |
+
)
|
| 676 |
+
|
| 677 |
+
gr.Markdown(
|
| 678 |
+
"""
|
| 679 |
+
<p style="text-align: center; margin-top: 10px; font-weight: 700; color: #1F2937;">
|
| 680 |
+
๐ก Right-click on the video to save, or use the download button!
|
| 681 |
+
</p>
|
| 682 |
+
"""
|
| 683 |
)
|
| 684 |
|
| 685 |
+
# Define inputs and outputs
|
| 686 |
+
inputs = [
|
| 687 |
+
prompt_input,
|
| 688 |
+
negative_prompt_input,
|
| 689 |
+
enhance_prompt_checkbox,
|
| 690 |
+
duration_slider,
|
| 691 |
+
guidance_scale_slider,
|
| 692 |
+
num_inference_steps_slider,
|
| 693 |
+
height_slider,
|
| 694 |
+
width_slider,
|
| 695 |
+
seed_slider,
|
| 696 |
+
randomize_seed_checkbox,
|
| 697 |
]
|
| 698 |
|
| 699 |
+
outputs = [
|
| 700 |
+
video_output,
|
| 701 |
+
seed_slider,
|
| 702 |
+
final_prompt_output,
|
| 703 |
+
info_log,
|
| 704 |
+
]
|
| 705 |
+
|
| 706 |
+
# Generate button click
|
| 707 |
+
generate_btn.click(
|
| 708 |
+
fn=generate_video,
|
| 709 |
+
inputs=inputs,
|
| 710 |
+
outputs=outputs,
|
| 711 |
)
|
| 712 |
|
| 713 |
+
gr.api(generate_video, api_name="generate_video")
|
| 714 |
+
|
| 715 |
if __name__ == "__main__":
|
| 716 |
demo.queue().launch()
|