Spaces:
Running on Zero
Running on Zero
bann commited on
Commit ·
feee32b
1
Parent(s): a2f22d5
feat(turbo): add native 4-step and 8-step LightX2V distill Turbo LoRA acceleration
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Wan 2.2 Space with ZeroGPU, Multi-LoRA (Civitai + HF), Presets, Trigger Words, and 300-Hours Optimizations."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
@@ -33,6 +33,35 @@ LOCAL_LORAS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lora
|
|
| 33 |
LORA_MAX_BYTES = 2 * 1024**3
|
| 34 |
DEFAULT_FPS = 16
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# =========================================================================
|
| 37 |
# Base NSFW Adapters (Unlocks adult generation on Wan Base Models)
|
| 38 |
# =========================================================================
|
|
@@ -312,6 +341,31 @@ def _download_lora_url(url: str, civitai_token: str = "") -> tuple[str, str]:
|
|
| 312 |
raise ValueError("Too many redirects downloading LoRA.")
|
| 313 |
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
def resolve_single_lora(
|
| 316 |
preset_type: str, custom_url: str, hf_repo: str, hf_file: str, local_file: str, strength: float, civitai_token: str = ""
|
| 317 |
) -> tuple[str | None, str, float]:
|
|
@@ -447,7 +501,7 @@ def _fit_keyframe(image_input, target_width: int, target_height: int) -> Image.I
|
|
| 447 |
|
| 448 |
def get_duration(*args, **kwargs):
|
| 449 |
try:
|
| 450 |
-
steps = kwargs.get("steps", args[6] if len(args) > 6 else
|
| 451 |
return max(180, min(MAX_GPU_DURATION, int(steps) * 8 + 60))
|
| 452 |
except Exception:
|
| 453 |
return 240
|
|
@@ -529,6 +583,7 @@ def generate_video(
|
|
| 529 |
prompt: str,
|
| 530 |
negative_prompt: str,
|
| 531 |
input_image: Image.Image | str | None,
|
|
|
|
| 532 |
nsfw_base_choice: str,
|
| 533 |
nsfw_base_strength: float,
|
| 534 |
canvas: str,
|
|
@@ -571,6 +626,12 @@ def generate_video(
|
|
| 571 |
lora_configs = []
|
| 572 |
active_labels = []
|
| 573 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
# 1. Base NSFW Adapter
|
| 575 |
if nsfw_base_choice in NSFW_BASE_ADAPTERS and nsfw_base_strength > 0:
|
| 576 |
base_spec = NSFW_BASE_ADAPTERS[nsfw_base_choice]
|
|
@@ -664,6 +725,11 @@ custom_css = """
|
|
| 664 |
font-weight: 600;
|
| 665 |
margin-right: 6px;
|
| 666 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 667 |
.badge-zerogpu {
|
| 668 |
background: rgba(16, 185, 129, 0.2);
|
| 669 |
color: #34d399;
|
|
@@ -711,17 +777,18 @@ all_preset_choices = (
|
|
| 711 |
+ (["Local File (loras/ folder)"] if get_local_loras() else [])
|
| 712 |
)
|
| 713 |
|
| 714 |
-
with gr.Blocks(css=custom_css, title="Wan 2.2 Video Studio") as app:
|
| 715 |
gr.HTML(
|
| 716 |
"""
|
| 717 |
<div class="header-card">
|
| 718 |
<div style="margin-bottom: 12px;">
|
|
|
|
| 719 |
<span class="badge badge-zerogpu">⚡ ZeroGPU</span>
|
| 720 |
<span class="badge badge-model">🎬 Wan 2.2 (MoE Diffusers)</span>
|
| 721 |
<span class="badge badge-multilora">🧩 Base NSFW + Multi-LoRA Engine</span>
|
| 722 |
</div>
|
| 723 |
-
<h1>Wan 2.2
|
| 724 |
-
<p>
|
| 725 |
</div>
|
| 726 |
"""
|
| 727 |
)
|
|
@@ -758,6 +825,14 @@ with gr.Blocks(css=custom_css, title="Wan 2.2 Video Studio") as app:
|
|
| 758 |
hmnsfw_example_btn = gr.Button("💡 Inserir Exemplo Completo de Prompt HMNSFW", elem_classes=["trigger-btn"])
|
| 759 |
|
| 760 |
with gr.Tabs():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
with gr.TabItem("🔞 Adaptador Base NSFW (Essencial)"):
|
| 762 |
gr.Markdown("Como o modelo base do Wan é treinado sem conteúdo explícito, este adaptador é carregado como base para desbloquear anatomia sem gastar seus slots de LoRA!")
|
| 763 |
with gr.Row():
|
|
@@ -883,11 +958,11 @@ with gr.Blocks(css=custom_css, title="Wan 2.2 Video Studio") as app:
|
|
| 883 |
)
|
| 884 |
steps = gr.Slider(
|
| 885 |
label="Passos de Inferência (Steps)",
|
| 886 |
-
minimum=
|
| 887 |
maximum=40,
|
| 888 |
step=1,
|
| 889 |
-
value=
|
| 890 |
-
info="
|
| 891 |
)
|
| 892 |
|
| 893 |
with gr.Row():
|
|
@@ -896,7 +971,8 @@ with gr.Blocks(css=custom_css, title="Wan 2.2 Video Studio") as app:
|
|
| 896 |
minimum=1.0,
|
| 897 |
maximum=10.0,
|
| 898 |
step=0.5,
|
| 899 |
-
value=
|
|
|
|
| 900 |
)
|
| 901 |
seed = gr.Number(label="Seed", value=42, precision=0)
|
| 902 |
randomize_seed = gr.Checkbox(label="🎲 Randomizar Seed", value=True)
|
|
@@ -908,13 +984,23 @@ with gr.Blocks(css=custom_css, title="Wan 2.2 Video Studio") as app:
|
|
| 908 |
type="password",
|
| 909 |
)
|
| 910 |
|
| 911 |
-
generate_btn = gr.Button("🚀 Gerar Vídeo Wan 2.2", variant="primary", elem_classes=["generate-btn"])
|
| 912 |
|
| 913 |
with gr.Column(scale=6):
|
| 914 |
output_video = gr.Video(label="Vídeo Gerado", autoplay=True, loop=True)
|
| 915 |
output_report = gr.Markdown(label="Detalhes da Geração", value="Pronto para renderizar vídeo.")
|
| 916 |
|
| 917 |
# Event handlers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 918 |
def on_lora1_change(preset_val):
|
| 919 |
spec = LORA_PRESETS.get(preset_val, {})
|
| 920 |
triggers = spec.get("trigger_words", "")
|
|
@@ -991,6 +1077,7 @@ with gr.Blocks(css=custom_css, title="Wan 2.2 Video Studio") as app:
|
|
| 991 |
prompt,
|
| 992 |
negative_prompt,
|
| 993 |
input_image,
|
|
|
|
| 994 |
nsfw_base_choice,
|
| 995 |
nsfw_base_strength,
|
| 996 |
canvas,
|
|
|
|
| 1 |
+
"""Wan 2.2 Space with ZeroGPU, Turbo 4-8 Step Distill, Multi-LoRA (Civitai + HF), Presets, Trigger Words, and 300-Hours Optimizations."""
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
| 33 |
LORA_MAX_BYTES = 2 * 1024**3
|
| 34 |
DEFAULT_FPS = 16
|
| 35 |
|
| 36 |
+
# =========================================================================
|
| 37 |
+
# Turbo Distillation LoRAs (4-Step & 8-Step Acceleration)
|
| 38 |
+
# =========================================================================
|
| 39 |
+
TURBO_PRESETS = {
|
| 40 |
+
"⚡ Turbo 4-Passos (LightX2V Distill - 4 Steps Ultra Rápido)": {
|
| 41 |
+
"wan2.2_i2v": ("lightx2v/Wan2.2-Distill-Loras", "wan2.2_i2v_A14b_high_noise_lora_rank64_lightx2v_4step_1022.safetensors"),
|
| 42 |
+
"wan2.2_t2v": ("lightx2v/Wan2.2-Distill-Loras", "wan2.2_t2v_A14b_high_noise_lora_rank64_lightx2v_4step_1217.safetensors"),
|
| 43 |
+
"wan2.1_i2v": ("lightx2v/Wan2.1-Distill-Loras", "wan2.1_i2v_lora_rank64_lightx2v_4step.safetensors"),
|
| 44 |
+
"wan2.1_t2v": ("lightx2v/Wan2.1-Distill-Loras", "wan2.1_t2v_14b_lora_rank64_lightx2v_4step.safetensors"),
|
| 45 |
+
"default_steps": 4,
|
| 46 |
+
"default_cfg": 1.5,
|
| 47 |
+
"description": "Destilação 4-passos LightX2V. Renderiza o vídeo em ~15-25 segundos mantendo alta qualidade!",
|
| 48 |
+
},
|
| 49 |
+
"⚡ Turbo 8-Passos (8 Steps Balanceado)": {
|
| 50 |
+
"wan2.2_i2v": ("lightx2v/Wan2.2-Distill-Loras", "wan2.2_i2v_A14b_low_noise_lora_rank64_lightx2v_4step_1022.safetensors"),
|
| 51 |
+
"wan2.2_t2v": ("lightx2v/Wan2.2-Distill-Loras", "wan2.2_t2v_A14b_low_noise_lora_rank64_lightx2v_4step_1217.safetensors"),
|
| 52 |
+
"wan2.1_i2v": ("lightx2v/Wan2.1-Distill-Loras", "wan2.1_i2v_lora_rank64_lightx2v_4step.safetensors"),
|
| 53 |
+
"wan2.1_t2v": ("lightx2v/Wan2.1-Distill-Loras", "wan2.1_t2v_14b_lora_rank64_lightx2v_4step.safetensors"),
|
| 54 |
+
"default_steps": 8,
|
| 55 |
+
"default_cfg": 2.0,
|
| 56 |
+
"description": "Destilação 8-passos para equilíbrio perfeito entre velocidade e detalhes.",
|
| 57 |
+
},
|
| 58 |
+
"🚫 Desativado (Modo Normal - 15 a 25 Passos)": {
|
| 59 |
+
"default_steps": 20,
|
| 60 |
+
"default_cfg": 5.0,
|
| 61 |
+
"description": "Modo padrão sem aceleração Turbo (requer 15-25 passos).",
|
| 62 |
+
},
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
# =========================================================================
|
| 66 |
# Base NSFW Adapters (Unlocks adult generation on Wan Base Models)
|
| 67 |
# =========================================================================
|
|
|
|
| 341 |
raise ValueError("Too many redirects downloading LoRA.")
|
| 342 |
|
| 343 |
|
| 344 |
+
def resolve_turbo_lora(turbo_choice: str, model_repo: str) -> tuple[str | None, float]:
|
| 345 |
+
"""Resolves and downloads the appropriate 4-step or 8-step distillation LoRA."""
|
| 346 |
+
if turbo_choice not in TURBO_PRESETS or "Desativado" in turbo_choice:
|
| 347 |
+
return None, 0.0
|
| 348 |
+
|
| 349 |
+
spec = TURBO_PRESETS[turbo_choice]
|
| 350 |
+
is_i2v = "I2V" in model_repo or "i2v" in model_repo or "TI2V" in model_repo
|
| 351 |
+
is_wan22 = "Wan2.2" in model_repo or "wan2.2" in model_repo
|
| 352 |
+
|
| 353 |
+
if is_wan22:
|
| 354 |
+
key = "wan2.2_i2v" if is_i2v else "wan2.2_t2v"
|
| 355 |
+
else:
|
| 356 |
+
key = "wan2.1_i2v" if is_i2v else "wan2.1_t2v"
|
| 357 |
+
|
| 358 |
+
if key not in spec:
|
| 359 |
+
return None, 0.0
|
| 360 |
+
|
| 361 |
+
repo_id, filename = spec[key]
|
| 362 |
+
from huggingface_hub import hf_hub_download
|
| 363 |
+
local_dir = _lora_cache_directory(f"hf://{repo_id}/{filename}")
|
| 364 |
+
path = hf_hub_download(repo_id=repo_id, filename=filename, token=False, local_dir=local_dir)
|
| 365 |
+
os.utime(local_dir, None)
|
| 366 |
+
return path, 1.0
|
| 367 |
+
|
| 368 |
+
|
| 369 |
def resolve_single_lora(
|
| 370 |
preset_type: str, custom_url: str, hf_repo: str, hf_file: str, local_file: str, strength: float, civitai_token: str = ""
|
| 371 |
) -> tuple[str | None, str, float]:
|
|
|
|
| 501 |
|
| 502 |
def get_duration(*args, **kwargs):
|
| 503 |
try:
|
| 504 |
+
steps = kwargs.get("steps", args[6] if len(args) > 6 else 4)
|
| 505 |
return max(180, min(MAX_GPU_DURATION, int(steps) * 8 + 60))
|
| 506 |
except Exception:
|
| 507 |
return 240
|
|
|
|
| 583 |
prompt: str,
|
| 584 |
negative_prompt: str,
|
| 585 |
input_image: Image.Image | str | None,
|
| 586 |
+
turbo_choice: str,
|
| 587 |
nsfw_base_choice: str,
|
| 588 |
nsfw_base_strength: float,
|
| 589 |
canvas: str,
|
|
|
|
| 626 |
lora_configs = []
|
| 627 |
active_labels = []
|
| 628 |
|
| 629 |
+
# 0. Turbo LoRA (4-Step or 8-Step Distill)
|
| 630 |
+
t_path, t_scale = resolve_turbo_lora(turbo_choice, model_choice)
|
| 631 |
+
if t_path and t_scale > 0:
|
| 632 |
+
lora_configs.append((t_path, t_scale))
|
| 633 |
+
active_labels.append(f"⚡ Turbo: {turbo_choice.split('(')[0].strip()}")
|
| 634 |
+
|
| 635 |
# 1. Base NSFW Adapter
|
| 636 |
if nsfw_base_choice in NSFW_BASE_ADAPTERS and nsfw_base_strength > 0:
|
| 637 |
base_spec = NSFW_BASE_ADAPTERS[nsfw_base_choice]
|
|
|
|
| 725 |
font-weight: 600;
|
| 726 |
margin-right: 6px;
|
| 727 |
}
|
| 728 |
+
.badge-turbo {
|
| 729 |
+
background: rgba(245, 158, 11, 0.2);
|
| 730 |
+
color: #fbbf24;
|
| 731 |
+
border: 1px solid rgba(245, 158, 11, 0.4);
|
| 732 |
+
}
|
| 733 |
.badge-zerogpu {
|
| 734 |
background: rgba(16, 185, 129, 0.2);
|
| 735 |
color: #34d399;
|
|
|
|
| 777 |
+ (["Local File (loras/ folder)"] if get_local_loras() else [])
|
| 778 |
)
|
| 779 |
|
| 780 |
+
with gr.Blocks(css=custom_css, title="Wan 2.2 Turbo Video Studio") as app:
|
| 781 |
gr.HTML(
|
| 782 |
"""
|
| 783 |
<div class="header-card">
|
| 784 |
<div style="margin-bottom: 12px;">
|
| 785 |
+
<span class="badge badge-turbo">⚡ Turbo 4-Step Distill</span>
|
| 786 |
<span class="badge badge-zerogpu">⚡ ZeroGPU</span>
|
| 787 |
<span class="badge badge-model">🎬 Wan 2.2 (MoE Diffusers)</span>
|
| 788 |
<span class="badge badge-multilora">🧩 Base NSFW + Multi-LoRA Engine</span>
|
| 789 |
</div>
|
| 790 |
+
<h1>Wan 2.2 Turbo Video Studio</h1>
|
| 791 |
+
<p>Aceleração Turbo de 4 a 8 Passos com Arquitetura Wan 2.2 MoE, Adaptador Base NSFW e Multi-LoRA.</p>
|
| 792 |
</div>
|
| 793 |
"""
|
| 794 |
)
|
|
|
|
| 825 |
hmnsfw_example_btn = gr.Button("💡 Inserir Exemplo Completo de Prompt HMNSFW", elem_classes=["trigger-btn"])
|
| 826 |
|
| 827 |
with gr.Tabs():
|
| 828 |
+
with gr.TabItem("⚡ Aceleração Turbo (4-8 Passos)"):
|
| 829 |
+
gr.Markdown("Acelera a geração em até **8x** aplicando destilação LightX2V. Permite renderizar vídeos em ~15 segundos!")
|
| 830 |
+
turbo_choice = gr.Dropdown(
|
| 831 |
+
label="Modo Turbo",
|
| 832 |
+
choices=list(TURBO_PRESETS.keys()),
|
| 833 |
+
value="⚡ Turbo 4-Passos (LightX2V Distill - 4 Steps Ultra Rápido)",
|
| 834 |
+
)
|
| 835 |
+
|
| 836 |
with gr.TabItem("🔞 Adaptador Base NSFW (Essencial)"):
|
| 837 |
gr.Markdown("Como o modelo base do Wan é treinado sem conteúdo explícito, este adaptador é carregado como base para desbloquear anatomia sem gastar seus slots de LoRA!")
|
| 838 |
with gr.Row():
|
|
|
|
| 958 |
)
|
| 959 |
steps = gr.Slider(
|
| 960 |
label="Passos de Inferência (Steps)",
|
| 961 |
+
minimum=2,
|
| 962 |
maximum=40,
|
| 963 |
step=1,
|
| 964 |
+
value=4,
|
| 965 |
+
info="Com Turbo 4-Passos ativado, 4 passos fornecem render ultrarrápido.",
|
| 966 |
)
|
| 967 |
|
| 968 |
with gr.Row():
|
|
|
|
| 971 |
minimum=1.0,
|
| 972 |
maximum=10.0,
|
| 973 |
step=0.5,
|
| 974 |
+
value=1.5,
|
| 975 |
+
info="No modo Turbo, recomenda-se CFG baixo (1.0 a 2.0).",
|
| 976 |
)
|
| 977 |
seed = gr.Number(label="Seed", value=42, precision=0)
|
| 978 |
randomize_seed = gr.Checkbox(label="🎲 Randomizar Seed", value=True)
|
|
|
|
| 984 |
type="password",
|
| 985 |
)
|
| 986 |
|
| 987 |
+
generate_btn = gr.Button("🚀 Gerar Vídeo Wan 2.2 Turbo", variant="primary", elem_classes=["generate-btn"])
|
| 988 |
|
| 989 |
with gr.Column(scale=6):
|
| 990 |
output_video = gr.Video(label="Vídeo Gerado", autoplay=True, loop=True)
|
| 991 |
output_report = gr.Markdown(label="Detalhes da Geração", value="Pronto para renderizar vídeo.")
|
| 992 |
|
| 993 |
# Event handlers
|
| 994 |
+
def on_turbo_change(turbo_val):
|
| 995 |
+
spec = TURBO_PRESETS.get(turbo_val, {})
|
| 996 |
+
return spec.get("default_steps", 4), spec.get("default_cfg", 1.5)
|
| 997 |
+
|
| 998 |
+
turbo_choice.change(
|
| 999 |
+
fn=on_turbo_change,
|
| 1000 |
+
inputs=[turbo_choice],
|
| 1001 |
+
outputs=[steps, guidance_scale],
|
| 1002 |
+
)
|
| 1003 |
+
|
| 1004 |
def on_lora1_change(preset_val):
|
| 1005 |
spec = LORA_PRESETS.get(preset_val, {})
|
| 1006 |
triggers = spec.get("trigger_words", "")
|
|
|
|
| 1077 |
prompt,
|
| 1078 |
negative_prompt,
|
| 1079 |
input_image,
|
| 1080 |
+
turbo_choice,
|
| 1081 |
nsfw_base_choice,
|
| 1082 |
nsfw_base_strength,
|
| 1083 |
canvas,
|