Spaces:
Sleeping
Sleeping
Download app.py from Isaacdnigerian/faceless: direct link, hf CLI and curl.
- Browser
- Download file 2.93 kB
-
https://huggingface.co/spaces/Isaacdnigerian/faceless/resolve/main/app.py
- Command line
-
hf download hf://spaces/Isaacdnigerian/faceless/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Isaacdnigerian/faceless/resolve/main/app.py
2.93 kB
| """ | |
| HF ZeroGPU Space: LTX-Video text-to-video — a free-GPU video backend for the | |
| faceless-reels pipeline (docs §9 hybrid). | |
| Exposes a Gradio API function `generate` that returns the MP4 as base64 (so the | |
| pipeline can decode it in src/lib/video.ts without downloading a hosted file). | |
| The @spaces.GPU decorator gives the function on-demand ZeroGPU access. | |
| Deploy: create a Gradio Space, set Hardware = ZeroGPU, add app.py + requirements.txt. | |
| See hf-space/README.md. | |
| """ | |
| import base64 | |
| import os | |
| import tempfile | |
| import gradio as gr | |
| import spaces | |
| import torch | |
| from diffusers import LTXPipeline | |
| from diffusers.utils import export_to_video | |
| MODEL_ID = os.environ.get("MODEL_ID", "Lightricks/LTX-Video") | |
| # Loaded on CPU at startup (ZeroGPU only exposes the GPU inside @spaces.GPU). | |
| # The weights download once when the Space boots. | |
| pipe = LTXPipeline.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16) | |
| # The LTX VAE decode in bf16 can emit NaN -> uniform black/gray/white frames. | |
| # Upcast the VAE to float32 to keep the decode numerically stable (the known fix | |
| # for LTX black-screen output). Transformer + text encoder stay bf16. | |
| pipe.vae.to(torch.float32) | |
| def _round32(x) -> int: | |
| return max(32, (int(x) // 32) * 32) | |
| def _num_frames(duration_sec: float, fps: int) -> int: | |
| n = int(round(duration_sec * fps)) | |
| return max(9, ((n - 1) // 8) * 8 + 1) # LTX requires num_frames % 8 == 1 | |
| def generate(prompt: str, width: float = 704, height: float = 1280, duration: float = 5) -> str: | |
| pipe.to("cuda") | |
| pipe.vae.to(torch.float32) # re-assert float32 VAE on-device (avoids black frames) | |
| fps = 24 | |
| result = pipe( | |
| prompt=prompt, | |
| negative_prompt="worst quality, inconsistent motion, blurry, jittery, distorted", | |
| width=_round32(width), | |
| height=_round32(height), | |
| num_frames=_num_frames(float(duration), fps), | |
| num_inference_steps=40, | |
| guidance_scale=3.0, | |
| # Timestep-aware VAE decode (LTX 0.9.1+): the documented values that | |
| # avoid black/NaN frames. | |
| decode_timestep=0.03, | |
| decode_noise_scale=0.025, | |
| ) | |
| frames = result.frames[0] | |
| path = tempfile.mktemp(suffix=".mp4") | |
| export_to_video(frames, path, fps=fps) | |
| with open(path, "rb") as f: | |
| b64 = base64.b64encode(f.read()).decode("utf-8") | |
| os.remove(path) | |
| return b64 | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Faceless LTX-Video\nReturns the generated clip as base64.") | |
| prompt = gr.Textbox(label="prompt") | |
| width = gr.Number(value=704, label="width") | |
| height = gr.Number(value=1280, label="height") | |
| duration = gr.Number(value=5, label="duration (s)") | |
| out = gr.Textbox(label="video_base64") | |
| btn = gr.Button("Generate") | |
| # api_name="generate" fixes the API route the pipeline calls. | |
| btn.click(generate, [prompt, width, height, duration], out, api_name="generate") | |
| demo.queue().launch() | |