Spaces:
Running on Zero
Running on Zero
bann commited on
Commit ·
6f83a93
1
Parent(s): 3573fa3
fix(gradio): fix OpenAPI bool schema crash and add Base NSFW Adapter slot with Civitai 1307155
Browse files
app.py
CHANGED
|
@@ -33,7 +33,31 @@ LORA_MAX_BYTES = 2 * 1024**3
|
|
| 33 |
DEFAULT_FPS = 16
|
| 34 |
|
| 35 |
# =========================================================================
|
| 36 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
# =========================================================================
|
| 38 |
LORA_PRESETS = {
|
| 39 |
"None / Desativado": {
|
|
@@ -42,6 +66,13 @@ LORA_PRESETS = {
|
|
| 42 |
"default_strength": 1.0,
|
| 43 |
"description": "Nenhum LoRA selecionado neste slot.",
|
| 44 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
"HMNSFW AIO V2 / hmmotion (Wan 2.1 / 2.2)": {
|
| 46 |
"type": "civitai",
|
| 47 |
"source": "https://civitai.com/api/download/models/3206518",
|
|
@@ -402,10 +433,10 @@ def _fit_keyframe(image_input, target_width: int, target_height: int) -> Image.I
|
|
| 402 |
return img
|
| 403 |
|
| 404 |
|
| 405 |
-
def get_duration(prompt, negative_prompt, input_image, canvas, num_frames, fps, steps, *a, **k):
|
| 406 |
steps = int(steps)
|
| 407 |
num_frames = int(num_frames)
|
| 408 |
-
h, w =
|
| 409 |
pixels_per_frame = h * w
|
| 410 |
total_tokens = (pixels_per_frame / 256) * (num_frames / 4)
|
| 411 |
estimated = int(steps * (total_tokens * 0.00012) + 20)
|
|
@@ -488,6 +519,8 @@ def generate_video(
|
|
| 488 |
prompt: str,
|
| 489 |
negative_prompt: str,
|
| 490 |
input_image: Image.Image | str | None,
|
|
|
|
|
|
|
| 491 |
canvas: str,
|
| 492 |
num_frames: int,
|
| 493 |
fps: int,
|
|
@@ -527,6 +560,17 @@ def generate_video(
|
|
| 527 |
lora_configs = []
|
| 528 |
active_labels = []
|
| 529 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 530 |
l1_path, l1_label, l1_scale = resolve_single_lora(
|
| 531 |
lora1_preset, lora1_custom_url, lora1_hf_repo, lora1_hf_file, lora1_local_file, lora1_strength, civitai_api_key
|
| 532 |
)
|
|
@@ -534,6 +578,7 @@ def generate_video(
|
|
| 534 |
lora_configs.append((l1_path, l1_scale))
|
| 535 |
active_labels.append(f"{l1_label} (@ {lora1_strength:g})")
|
| 536 |
|
|
|
|
| 537 |
l2_path, l2_label, l2_scale = resolve_single_lora(
|
| 538 |
lora2_preset, lora2_custom_url, lora2_hf_repo, lora2_hf_file, lora2_local_file, lora2_strength, civitai_api_key
|
| 539 |
)
|
|
@@ -661,11 +706,11 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 661 |
<div class="header-card">
|
| 662 |
<div style="margin-bottom: 12px;">
|
| 663 |
<span class="badge badge-zerogpu">⚡ ZeroGPU</span>
|
| 664 |
-
<span class="badge badge-model">🎬 Wan 2.1
|
| 665 |
-
<span class="badge badge-multilora">🧩 Multi-LoRA Engine</span>
|
| 666 |
</div>
|
| 667 |
<h1>Wan 2.1 / 2.2 AI Video Studio</h1>
|
| 668 |
-
<p>Image-to-Video (I2V) & Text-to-Video (T2V) com
|
| 669 |
</div>
|
| 670 |
"""
|
| 671 |
)
|
|
@@ -676,14 +721,14 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 676 |
label="Prompt",
|
| 677 |
placeholder="Descreva a cena detalhada do vídeo...",
|
| 678 |
lines=4,
|
| 679 |
-
value="
|
| 680 |
)
|
| 681 |
|
| 682 |
negative_prompt = gr.Textbox(
|
| 683 |
label="Negative Prompt",
|
| 684 |
placeholder="Elementos indesejados (distorções, membros extras, baixa qualidade)...",
|
| 685 |
lines=2,
|
| 686 |
-
value="deformed, bad anatomy, extra limbs, blurry, low resolution, bad quality, distortion",
|
| 687 |
)
|
| 688 |
|
| 689 |
with gr.Accordion("📖 Guia & Modelos de Prompt HMNSFW / hmmotion (Wan 2.1 / 2.2)", open=False):
|
|
@@ -702,15 +747,32 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 702 |
hmnsfw_example_btn = gr.Button("💡 Inserir Exemplo Completo de Prompt HMNSFW", elem_classes=["trigger-btn"])
|
| 703 |
|
| 704 |
with gr.Tabs():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 705 |
with gr.TabItem("🖼️ Image-to-Video (I2V)"):
|
| 706 |
gr.Markdown("Faça upload de uma imagem inicial (`First Frame`). O sistema ajustará o corte automaticamente para a proporção múltipla de 16 selecionada.")
|
| 707 |
-
input_image = gr.Image(label="First Frame (Imagem Inicial)", type="
|
| 708 |
|
| 709 |
-
with gr.TabItem("🎨 LoRA Slot 1 (
|
| 710 |
lora1_preset = gr.Dropdown(
|
| 711 |
label="LoRA Slot 1 Preset / Fonte",
|
| 712 |
choices=all_preset_choices,
|
| 713 |
-
value="
|
| 714 |
)
|
| 715 |
with gr.Group(visible=False) as lora1_custom_url_grp:
|
| 716 |
lora1_custom_url = gr.Textbox(
|
|
@@ -726,7 +788,7 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 726 |
|
| 727 |
lora1_trigger_display = gr.Textbox(
|
| 728 |
label="Trigger Words",
|
| 729 |
-
value="",
|
| 730 |
interactive=False,
|
| 731 |
)
|
| 732 |
add_lora1_triggers_btn = gr.Button("📋 Inserir Trigger Words no Prompt", elem_classes=["trigger-btn"])
|
|
@@ -736,7 +798,7 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 736 |
minimum=0.0,
|
| 737 |
maximum=2.0,
|
| 738 |
step=0.05,
|
| 739 |
-
value=
|
| 740 |
)
|
| 741 |
|
| 742 |
with gr.TabItem("🎬 LoRA Slot 2 (Movimento / Câmera)"):
|
|
@@ -855,6 +917,7 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 855 |
fn=on_lora1_change,
|
| 856 |
inputs=[lora1_preset],
|
| 857 |
outputs=[lora1_custom_url_grp, lora1_hf_grp, lora1_local_grp, lora1_trigger_display, lora1_strength],
|
|
|
|
| 858 |
)
|
| 859 |
|
| 860 |
def on_lora2_change(preset_val):
|
|
@@ -873,6 +936,7 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 873 |
fn=on_lora2_change,
|
| 874 |
inputs=[lora2_preset],
|
| 875 |
outputs=[lora2_custom_url_grp, lora2_hf_grp, lora2_local_grp, lora2_trigger_display, lora2_strength],
|
|
|
|
| 876 |
)
|
| 877 |
|
| 878 |
def append_triggers(curr_prompt, triggers):
|
|
@@ -904,9 +968,10 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 904 |
"The setting is a bed with dark grey sheets under warm, low indoor lighting."
|
| 905 |
)
|
| 906 |
|
| 907 |
-
hmnsfw_example_btn.click(fn=load_hmnsfw_example, outputs=[prompt])
|
| 908 |
-
|
| 909 |
-
|
|
|
|
| 910 |
|
| 911 |
generate_btn.click(
|
| 912 |
fn=generate_video,
|
|
@@ -914,6 +979,8 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 914 |
prompt,
|
| 915 |
negative_prompt,
|
| 916 |
input_image,
|
|
|
|
|
|
|
| 917 |
canvas,
|
| 918 |
num_frames,
|
| 919 |
fps,
|
|
@@ -937,7 +1004,8 @@ with gr.Blocks(css=custom_css, title="Wan 2.1 / 2.2 Video Studio") as app:
|
|
| 937 |
civitai_api_key,
|
| 938 |
],
|
| 939 |
outputs=[output_video, output_report, seed],
|
|
|
|
| 940 |
)
|
| 941 |
|
| 942 |
if __name__ == "__main__":
|
| 943 |
-
app.launch(show_error=True, allowed_paths=[OUTPUT_DIR])
|
|
|
|
| 33 |
DEFAULT_FPS = 16
|
| 34 |
|
| 35 |
# =========================================================================
|
| 36 |
+
# Base NSFW Adapters (Unlocks adult generation on Wan Base Models)
|
| 37 |
+
# =========================================================================
|
| 38 |
+
NSFW_BASE_ADAPTERS = {
|
| 39 |
+
"WAN General NSFW Model (Civitai 1307155 - Recomendado)": {
|
| 40 |
+
"source": "https://civitai.com/models/1307155",
|
| 41 |
+
"trigger_words": "nsfwsks, uncensored, naked, explicit",
|
| 42 |
+
"default_strength": 1.0,
|
| 43 |
+
"description": "Desbloqueia anatomia e geração explícita no modelo base Wan (Civitai Model 1307155).",
|
| 44 |
+
},
|
| 45 |
+
"HMNSFW AIO V2 Base (Civitai 2834417)": {
|
| 46 |
+
"source": "https://civitai.com/api/download/models/3206518",
|
| 47 |
+
"trigger_words": "hmmotion",
|
| 48 |
+
"default_strength": 0.5,
|
| 49 |
+
"description": "Adaptador anatômico geral HMNSFW V2 para Wan 2.1/2.2.",
|
| 50 |
+
},
|
| 51 |
+
"Desativado / Apenas Base SFW": {
|
| 52 |
+
"source": "",
|
| 53 |
+
"trigger_words": "",
|
| 54 |
+
"default_strength": 0.0,
|
| 55 |
+
"description": "Usa apenas o modelo base padrão sem adaptador NSFW adicional.",
|
| 56 |
+
},
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
# =========================================================================
|
| 60 |
+
# Preset Catalog for Wan 2.1 / 2.2 LoRAs (Actions, Motions & Styles)
|
| 61 |
# =========================================================================
|
| 62 |
LORA_PRESETS = {
|
| 63 |
"None / Desativado": {
|
|
|
|
| 66 |
"default_strength": 1.0,
|
| 67 |
"description": "Nenhum LoRA selecionado neste slot.",
|
| 68 |
},
|
| 69 |
+
"WAN General NSFW model (Civitai 1307155)": {
|
| 70 |
+
"type": "civitai",
|
| 71 |
+
"source": "https://civitai.com/models/1307155",
|
| 72 |
+
"trigger_words": "nsfwsks, uncensored, naked, explicit",
|
| 73 |
+
"default_strength": 1.0,
|
| 74 |
+
"description": "LoRA Geral NSFW para Wan 2.1 / 2.2 (Civitai 1307155).",
|
| 75 |
+
},
|
| 76 |
"HMNSFW AIO V2 / hmmotion (Wan 2.1 / 2.2)": {
|
| 77 |
"type": "civitai",
|
| 78 |
"source": "https://civitai.com/api/download/models/3206518",
|
|
|
|
| 433 |
return img
|
| 434 |
|
| 435 |
|
| 436 |
+
def get_duration(prompt, negative_prompt, input_image, nsfw_base_choice, nsfw_base_strength, canvas, num_frames, fps, steps, *a, **k):
|
| 437 |
steps = int(steps)
|
| 438 |
num_frames = int(num_frames)
|
| 439 |
+
h, w = CANVASES.get(canvas, (480, 832))
|
| 440 |
pixels_per_frame = h * w
|
| 441 |
total_tokens = (pixels_per_frame / 256) * (num_frames / 4)
|
| 442 |
estimated = int(steps * (total_tokens * 0.00012) + 20)
|
|
|
|
| 519 |
prompt: str,
|
| 520 |
negative_prompt: str,
|
| 521 |
input_image: Image.Image | str | None,
|
| 522 |
+
nsfw_base_choice: str,
|
| 523 |
+
nsfw_base_strength: float,
|
| 524 |
canvas: str,
|
| 525 |
num_frames: int,
|
| 526 |
fps: int,
|
|
|
|
| 560 |
lora_configs = []
|
| 561 |
active_labels = []
|
| 562 |
|
| 563 |
+
# 1. Base NSFW Adapter
|
| 564 |
+
if nsfw_base_choice in NSFW_BASE_ADAPTERS and nsfw_base_strength > 0:
|
| 565 |
+
base_spec = NSFW_BASE_ADAPTERS[nsfw_base_choice]
|
| 566 |
+
if base_spec.get("source"):
|
| 567 |
+
b_url = normalize_civitai_url(base_spec["source"])
|
| 568 |
+
if b_url:
|
| 569 |
+
b_path, b_label = _download_lora_url(b_url, civitai_api_key)
|
| 570 |
+
lora_configs.append((b_path, float(nsfw_base_strength)))
|
| 571 |
+
active_labels.append(f"🔞 NSFW Base: {nsfw_base_choice.split('(')[0].strip()} (@ {nsfw_base_strength:g})")
|
| 572 |
+
|
| 573 |
+
# 2. Slot 1 LoRA
|
| 574 |
l1_path, l1_label, l1_scale = resolve_single_lora(
|
| 575 |
lora1_preset, lora1_custom_url, lora1_hf_repo, lora1_hf_file, lora1_local_file, lora1_strength, civitai_api_key
|
| 576 |
)
|
|
|
|
| 578 |
lora_configs.append((l1_path, l1_scale))
|
| 579 |
active_labels.append(f"{l1_label} (@ {lora1_strength:g})")
|
| 580 |
|
| 581 |
+
# 3. Slot 2 LoRA
|
| 582 |
l2_path, l2_label, l2_scale = resolve_single_lora(
|
| 583 |
lora2_preset, lora2_custom_url, lora2_hf_repo, lora2_hf_file, lora2_local_file, lora2_strength, civitai_api_key
|
| 584 |
)
|
|
|
|
| 706 |
<div class="header-card">
|
| 707 |
<div style="margin-bottom: 12px;">
|
| 708 |
<span class="badge badge-zerogpu">⚡ ZeroGPU</span>
|
| 709 |
+
<span class="badge badge-model">🎬 Wan 2.1 / 2.2 (Diffusers)</span>
|
| 710 |
+
<span class="badge badge-multilora">🧩 Base NSFW + Multi-LoRA Engine</span>
|
| 711 |
</div>
|
| 712 |
<h1>Wan 2.1 / 2.2 AI Video Studio</h1>
|
| 713 |
+
<p>Image-to-Video (I2V) & Text-to-Video (T2V) com Adaptador NSFW Base, Multi-LoRA e Resoluções Múltiplas de 16.</p>
|
| 714 |
</div>
|
| 715 |
"""
|
| 716 |
)
|
|
|
|
| 721 |
label="Prompt",
|
| 722 |
placeholder="Descreva a cena detalhada do vídeo...",
|
| 723 |
lines=4,
|
| 724 |
+
value="nsfwsks, a cinematic shot of a beautiful woman with glowing eyes, dramatic lighting, 8k masterpiece",
|
| 725 |
)
|
| 726 |
|
| 727 |
negative_prompt = gr.Textbox(
|
| 728 |
label="Negative Prompt",
|
| 729 |
placeholder="Elementos indesejados (distorções, membros extras, baixa qualidade)...",
|
| 730 |
lines=2,
|
| 731 |
+
value="deformed, bad anatomy, extra limbs, blurry, low resolution, bad quality, distortion, censorship",
|
| 732 |
)
|
| 733 |
|
| 734 |
with gr.Accordion("📖 Guia & Modelos de Prompt HMNSFW / hmmotion (Wan 2.1 / 2.2)", open=False):
|
|
|
|
| 747 |
hmnsfw_example_btn = gr.Button("💡 Inserir Exemplo Completo de Prompt HMNSFW", elem_classes=["trigger-btn"])
|
| 748 |
|
| 749 |
with gr.Tabs():
|
| 750 |
+
with gr.TabItem("🔞 Adaptador Base NSFW (Essencial)"):
|
| 751 |
+
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!")
|
| 752 |
+
with gr.Row():
|
| 753 |
+
nsfw_base_choice = gr.Dropdown(
|
| 754 |
+
label="Adaptador Base NSFW",
|
| 755 |
+
choices=list(NSFW_BASE_ADAPTERS.keys()),
|
| 756 |
+
value="WAN General NSFW Model (Civitai 1307155 - Recomendado)",
|
| 757 |
+
)
|
| 758 |
+
nsfw_base_strength = gr.Slider(
|
| 759 |
+
label="Força do Adaptador Base NSFW",
|
| 760 |
+
minimum=0.0,
|
| 761 |
+
maximum=1.5,
|
| 762 |
+
step=0.05,
|
| 763 |
+
value=1.0,
|
| 764 |
+
)
|
| 765 |
+
nsfw_base_trigger_btn = gr.Button("📋 Inserir Trigger `nsfwsks` no Prompt", elem_classes=["trigger-btn"])
|
| 766 |
+
|
| 767 |
with gr.TabItem("🖼️ Image-to-Video (I2V)"):
|
| 768 |
gr.Markdown("Faça upload de uma imagem inicial (`First Frame`). O sistema ajustará o corte automaticamente para a proporção múltipla de 16 selecionada.")
|
| 769 |
+
input_image = gr.Image(label="First Frame (Imagem Inicial)", type="pil")
|
| 770 |
|
| 771 |
+
with gr.TabItem("🎨 LoRA Slot 1 (Ação / Pose)"):
|
| 772 |
lora1_preset = gr.Dropdown(
|
| 773 |
label="LoRA Slot 1 Preset / Fonte",
|
| 774 |
choices=all_preset_choices,
|
| 775 |
+
value="HMNSFW AIO V2 / hmmotion (Wan 2.1 / 2.2)",
|
| 776 |
)
|
| 777 |
with gr.Group(visible=False) as lora1_custom_url_grp:
|
| 778 |
lora1_custom_url = gr.Textbox(
|
|
|
|
| 788 |
|
| 789 |
lora1_trigger_display = gr.Textbox(
|
| 790 |
label="Trigger Words",
|
| 791 |
+
value=LORA_PRESETS.get("HMNSFW AIO V2 / hmmotion (Wan 2.1 / 2.2)", {}).get("trigger_words", ""),
|
| 792 |
interactive=False,
|
| 793 |
)
|
| 794 |
add_lora1_triggers_btn = gr.Button("📋 Inserir Trigger Words no Prompt", elem_classes=["trigger-btn"])
|
|
|
|
| 798 |
minimum=0.0,
|
| 799 |
maximum=2.0,
|
| 800 |
step=0.05,
|
| 801 |
+
value=0.5,
|
| 802 |
)
|
| 803 |
|
| 804 |
with gr.TabItem("🎬 LoRA Slot 2 (Movimento / Câmera)"):
|
|
|
|
| 917 |
fn=on_lora1_change,
|
| 918 |
inputs=[lora1_preset],
|
| 919 |
outputs=[lora1_custom_url_grp, lora1_hf_grp, lora1_local_grp, lora1_trigger_display, lora1_strength],
|
| 920 |
+
api_name=False,
|
| 921 |
)
|
| 922 |
|
| 923 |
def on_lora2_change(preset_val):
|
|
|
|
| 936 |
fn=on_lora2_change,
|
| 937 |
inputs=[lora2_preset],
|
| 938 |
outputs=[lora2_custom_url_grp, lora2_hf_grp, lora2_local_grp, lora2_trigger_display, lora2_strength],
|
| 939 |
+
api_name=False,
|
| 940 |
)
|
| 941 |
|
| 942 |
def append_triggers(curr_prompt, triggers):
|
|
|
|
| 968 |
"The setting is a bed with dark grey sheets under warm, low indoor lighting."
|
| 969 |
)
|
| 970 |
|
| 971 |
+
hmnsfw_example_btn.click(fn=load_hmnsfw_example, outputs=[prompt], api_name=False)
|
| 972 |
+
nsfw_base_trigger_btn.click(fn=lambda p: append_triggers(p, "nsfwsks, uncensored, explicit"), inputs=[prompt], outputs=[prompt], api_name=False)
|
| 973 |
+
add_lora1_triggers_btn.click(fn=append_triggers, inputs=[prompt, lora1_trigger_display], outputs=[prompt], api_name=False)
|
| 974 |
+
add_lora2_triggers_btn.click(fn=append_triggers, inputs=[prompt, lora2_trigger_display], outputs=[prompt], api_name=False)
|
| 975 |
|
| 976 |
generate_btn.click(
|
| 977 |
fn=generate_video,
|
|
|
|
| 979 |
prompt,
|
| 980 |
negative_prompt,
|
| 981 |
input_image,
|
| 982 |
+
nsfw_base_choice,
|
| 983 |
+
nsfw_base_strength,
|
| 984 |
canvas,
|
| 985 |
num_frames,
|
| 986 |
fps,
|
|
|
|
| 1004 |
civitai_api_key,
|
| 1005 |
],
|
| 1006 |
outputs=[output_video, output_report, seed],
|
| 1007 |
+
api_name="generate_video",
|
| 1008 |
)
|
| 1009 |
|
| 1010 |
if __name__ == "__main__":
|
| 1011 |
+
app.launch(show_api=False, show_error=True, allowed_paths=[OUTPUT_DIR])
|