Spaces:
Running on Zero
Running on Zero
Video retention 25→200 + NSFW enhancer checkboxes
Browse files- Increased output video rotation limit from 25 to 200 so older generations
survive longer in /tmp before cleanup.
- Replaced auto-loading of nsfw_aio whenever any NSFW LoRA is selected with
two explicit checkboxes (mutually exclusive):
• HMNSFW AIO V2.5 (motion adapter)
• Astro NSFW (quality enhancer)
Users now choose which enhancer to pair with their selected LoRAs, or none.
app.py
CHANGED
|
@@ -2158,7 +2158,7 @@ def _generate(prompt_embeds, text_token_tags, image, last_image, height, width,
|
|
| 2158 |
return _videos, _audio, _sampling_rate
|
| 2159 |
|
| 2160 |
|
| 2161 |
-
def _generate_impl(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=25, seed=-1, upsample=False, turbo=False, nsfw_lora_keys=(), custom_lora_url="", custom_lora_scale=1.0, custom_lora_trigger="", progress=gr.Progress(track_tqdm=True)):
|
| 2162 |
"""The request pipeline: LoRA prep → remote conditioning → @spaces.GPU denoise → mux."""
|
| 2163 |
if LOAD_ERROR:
|
| 2164 |
raise gr.Error(LOAD_ERROR)
|
|
@@ -2212,8 +2212,10 @@ def _generate_impl(prompt, image_path=None, last_image_path=None, canvas=DEFAULT
|
|
| 2212 |
_trigger = str(custom_lora_trigger or "").strip()
|
| 2213 |
custom = [(_repo, _file, _scale, _trigger)]
|
| 2214 |
|
| 2215 |
-
if
|
| 2216 |
nsfw_keys += ("nsfw_aio",)
|
|
|
|
|
|
|
| 2217 |
for _key in nsfw_keys:
|
| 2218 |
_ensure_nsfw_lora_loaded(_key)
|
| 2219 |
for _key in nsfw_keys:
|
|
@@ -2262,7 +2264,7 @@ def _generate_impl(prompt, image_path=None, last_image_path=None, canvas=DEFAULT
|
|
| 2262 |
directory = os.path.join(tempfile.gettempdir(), "h3-outputs")
|
| 2263 |
os.makedirs(directory, exist_ok=True)
|
| 2264 |
# Rotate: keep only the newest _MAX_OUTPUTS MP4s so /tmp never fills up.
|
| 2265 |
-
_MAX_OUTPUTS =
|
| 2266 |
for _f in sorted(glob.glob(os.path.join(directory, "*.mp4")), key=os.path.getmtime)[:-_MAX_OUTPUTS]:
|
| 2267 |
try:
|
| 2268 |
os.remove(_f)
|
|
@@ -2292,7 +2294,7 @@ def _generate_impl(prompt, image_path=None, last_image_path=None, canvas=DEFAULT
|
|
| 2292 |
return path, report, refined, gr.update(visible=bool(refined)), gr.DownloadButton(value=path, visible=True), gr.update(value=lora_label_text, visible=bool(nsfw_keys) or bool(custom))
|
| 2293 |
|
| 2294 |
|
| 2295 |
-
def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=25, seed=-1, upsample=False, turbo=False, nsfw_lora_keys=(), custom_lora_url="", custom_lora_scale=1.0, custom_lora_trigger="", progress=gr.Progress(track_tqdm=True)):
|
| 2296 |
"""One request. `upsample` is last among the pre-existing positional args, so a positional API client that
|
| 2297 |
predates it is unaffected; the custom-LoRA args come after it.
|
| 2298 |
|
|
@@ -2307,7 +2309,8 @@ def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVA
|
|
| 2307 |
try:
|
| 2308 |
return _generate_impl(
|
| 2309 |
prompt, image_path, last_image_path, canvas, duration, steps, seed, upsample, turbo,
|
| 2310 |
-
nsfw_lora_keys, custom_lora_url, custom_lora_scale, custom_lora_trigger,
|
|
|
|
| 2311 |
)
|
| 2312 |
except gr.Error as _e:
|
| 2313 |
_log(f"generate rejected: {_e}", "WARN")
|
|
@@ -2651,13 +2654,24 @@ with gr.Blocks(title="minimax h3 turbo + lora's") as demo:
|
|
| 2651 |
with gr.Accordion("NSFW LoRAs", open=False):
|
| 2652 |
nsfw_lora_keys = gr.Dropdown(
|
| 2653 |
label="Select LoRAs to apply",
|
| 2654 |
-
choices=[(cfg["label"], key) for key, cfg in NSFW_LORA_CONFIGS.items() if key
|
| 2655 |
value=[],
|
| 2656 |
multiselect=True,
|
| 2657 |
filterable=True,
|
| 2658 |
-
info="Searchable — type to filter. Selected LoRAs show as removable tags; their trigger tokens are auto-appended to the prompt.
|
| 2659 |
)
|
| 2660 |
lora_desc = gr.Markdown(visible=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2661 |
with gr.Accordion("Custom LoRA (Hugging Face)", open=False):
|
| 2662 |
gr.Markdown("Paste a Hugging Face link — a repo page or a direct `.safetensors` file link — then press "
|
| 2663 |
"**Download & Verify**. The verified LoRA is applied on Generate. "
|
|
@@ -2713,7 +2727,7 @@ with gr.Blocks(title="minimax h3 turbo + lora's") as demo:
|
|
| 2713 |
run.click(
|
| 2714 |
generate,
|
| 2715 |
[prompt, image, last_image, canvas, duration, steps, seed, upsample, turbo, nsfw_lora_keys,
|
| 2716 |
-
custom_lora_url, custom_lora_scale, custom_lora_trigger],
|
| 2717 |
[video, report, upsampled, upsampled_panel, download_btn, lora_label],
|
| 2718 |
api_name="generate",
|
| 2719 |
)
|
|
@@ -2729,6 +2743,16 @@ with gr.Blocks(title="minimax h3 turbo + lora's") as demo:
|
|
| 2729 |
|
| 2730 |
nsfw_lora_keys.change(_lora_descriptions, [nsfw_lora_keys], [lora_desc])
|
| 2731 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2732 |
last_frame_btn.click(
|
| 2733 |
export_last_frame,
|
| 2734 |
[video],
|
|
|
|
| 2158 |
return _videos, _audio, _sampling_rate
|
| 2159 |
|
| 2160 |
|
| 2161 |
+
def _generate_impl(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=25, seed=-1, upsample=False, turbo=False, nsfw_lora_keys=(), custom_lora_url="", custom_lora_scale=1.0, custom_lora_trigger="", nsfw_aio_enabled=False, astro_nsfw_enabled=False, progress=gr.Progress(track_tqdm=True)):
|
| 2162 |
"""The request pipeline: LoRA prep → remote conditioning → @spaces.GPU denoise → mux."""
|
| 2163 |
if LOAD_ERROR:
|
| 2164 |
raise gr.Error(LOAD_ERROR)
|
|
|
|
| 2212 |
_trigger = str(custom_lora_trigger or "").strip()
|
| 2213 |
custom = [(_repo, _file, _scale, _trigger)]
|
| 2214 |
|
| 2215 |
+
if nsfw_aio_enabled and "nsfw_aio" not in nsfw_keys:
|
| 2216 |
nsfw_keys += ("nsfw_aio",)
|
| 2217 |
+
if astro_nsfw_enabled and "nsfw_astro_nsfw" not in nsfw_keys:
|
| 2218 |
+
nsfw_keys += ("nsfw_astro_nsfw",)
|
| 2219 |
for _key in nsfw_keys:
|
| 2220 |
_ensure_nsfw_lora_loaded(_key)
|
| 2221 |
for _key in nsfw_keys:
|
|
|
|
| 2264 |
directory = os.path.join(tempfile.gettempdir(), "h3-outputs")
|
| 2265 |
os.makedirs(directory, exist_ok=True)
|
| 2266 |
# Rotate: keep only the newest _MAX_OUTPUTS MP4s so /tmp never fills up.
|
| 2267 |
+
_MAX_OUTPUTS = 200
|
| 2268 |
for _f in sorted(glob.glob(os.path.join(directory, "*.mp4")), key=os.path.getmtime)[:-_MAX_OUTPUTS]:
|
| 2269 |
try:
|
| 2270 |
os.remove(_f)
|
|
|
|
| 2294 |
return path, report, refined, gr.update(visible=bool(refined)), gr.DownloadButton(value=path, visible=True), gr.update(value=lora_label_text, visible=bool(nsfw_keys) or bool(custom))
|
| 2295 |
|
| 2296 |
|
| 2297 |
+
def generate(prompt, image_path=None, last_image_path=None, canvas=DEFAULT_CANVAS, duration=5, steps=25, seed=-1, upsample=False, turbo=False, nsfw_lora_keys=(), custom_lora_url="", custom_lora_scale=1.0, custom_lora_trigger="", nsfw_aio_enabled=False, astro_nsfw_enabled=False, progress=gr.Progress(track_tqdm=True)):
|
| 2298 |
"""One request. `upsample` is last among the pre-existing positional args, so a positional API client that
|
| 2299 |
predates it is unaffected; the custom-LoRA args come after it.
|
| 2300 |
|
|
|
|
| 2309 |
try:
|
| 2310 |
return _generate_impl(
|
| 2311 |
prompt, image_path, last_image_path, canvas, duration, steps, seed, upsample, turbo,
|
| 2312 |
+
nsfw_lora_keys, custom_lora_url, custom_lora_scale, custom_lora_trigger,
|
| 2313 |
+
nsfw_aio_enabled, astro_nsfw_enabled, progress,
|
| 2314 |
)
|
| 2315 |
except gr.Error as _e:
|
| 2316 |
_log(f"generate rejected: {_e}", "WARN")
|
|
|
|
| 2654 |
with gr.Accordion("NSFW LoRAs", open=False):
|
| 2655 |
nsfw_lora_keys = gr.Dropdown(
|
| 2656 |
label="Select LoRAs to apply",
|
| 2657 |
+
choices=[(cfg["label"], key) for key, cfg in NSFW_LORA_CONFIGS.items() if key not in ("nsfw_aio", "nsfw_astro_nsfw")],
|
| 2658 |
value=[],
|
| 2659 |
multiselect=True,
|
| 2660 |
filterable=True,
|
| 2661 |
+
info="Searchable — type to filter. Selected LoRAs show as removable tags; their trigger tokens are auto-appended to the prompt. Pick an enhancer adapter below.",
|
| 2662 |
)
|
| 2663 |
lora_desc = gr.Markdown(visible=False)
|
| 2664 |
+
with gr.Row():
|
| 2665 |
+
nsfw_aio_cb = gr.Checkbox(
|
| 2666 |
+
label="HMNSFW AIO V2.5 (motion adapter)",
|
| 2667 |
+
value=False,
|
| 2668 |
+
info="Auto-loads the Hearmeman motion adapter — makes anatomy LoRAs move naturally. Select one enhancer only.",
|
| 2669 |
+
)
|
| 2670 |
+
astro_nsfw_cb = gr.Checkbox(
|
| 2671 |
+
label="Astro NSFW (quality enhancer)",
|
| 2672 |
+
value=False,
|
| 2673 |
+
info="General NSFW quality enhancer that improves human movement and anatomy. Select one enhancer only.",
|
| 2674 |
+
)
|
| 2675 |
with gr.Accordion("Custom LoRA (Hugging Face)", open=False):
|
| 2676 |
gr.Markdown("Paste a Hugging Face link — a repo page or a direct `.safetensors` file link — then press "
|
| 2677 |
"**Download & Verify**. The verified LoRA is applied on Generate. "
|
|
|
|
| 2727 |
run.click(
|
| 2728 |
generate,
|
| 2729 |
[prompt, image, last_image, canvas, duration, steps, seed, upsample, turbo, nsfw_lora_keys,
|
| 2730 |
+
custom_lora_url, custom_lora_scale, custom_lora_trigger, nsfw_aio_cb, astro_nsfw_cb],
|
| 2731 |
[video, report, upsampled, upsampled_panel, download_btn, lora_label],
|
| 2732 |
api_name="generate",
|
| 2733 |
)
|
|
|
|
| 2743 |
|
| 2744 |
nsfw_lora_keys.change(_lora_descriptions, [nsfw_lora_keys], [lora_desc])
|
| 2745 |
|
| 2746 |
+
# Mutual exclusivity: checking one NSFW enhancer unchecks the other.
|
| 2747 |
+
nsfw_aio_cb.change(
|
| 2748 |
+
lambda v: gr.Checkbox(value=False) if v else gr.update(),
|
| 2749 |
+
[nsfw_aio_cb], [astro_nsfw_cb], api_name=False,
|
| 2750 |
+
)
|
| 2751 |
+
astro_nsfw_cb.change(
|
| 2752 |
+
lambda v: gr.Checkbox(value=False) if v else gr.update(),
|
| 2753 |
+
[astro_nsfw_cb], [nsfw_aio_cb], api_name=False,
|
| 2754 |
+
)
|
| 2755 |
+
|
| 2756 |
last_frame_btn.click(
|
| 2757 |
export_last_frame,
|
| 2758 |
[video],
|