import gradio as gr
from typing import Optional, List, Dict, Any
import numpy as np
import time
import sys
from pathlib import Path
# CRITICAL FIX: Ensure local modules are prioritized over system packages
# This prevents ImportError when a 'utils' package exists in site-packages
current_dir = Path(__file__).resolve().parent
if str(current_dir) not in sys.path:
sys.path.insert(0, str(current_dir))
# Clear any cached utils module to force reimport from local file
if 'utils' in sys.modules:
del sys.modules['utils']
# Now import from the local utils.py file
from utils import WANVideoGenerator, LoRAManager, NSFWChecker
from config import MODEL_CONFIGS, AVAILABLE_LORAS, NSFW_CONFIG
# Initialize core components
generator = WANVideoGenerator()
lora_manager = LoRAManager()
nsfw_checker = NSFWChecker()
def generate_video(
image: np.ndarray,
prompt: str,
selected_model: str,
enabled_loras: List[str],
enable_nsfw: bool,
video_length: int,
resolution: str,
progress=gr.Progress()
) -> tuple[str, str, Dict[str, Any]]:
"""
Main video generation function with WAN-scale processing
Args:
image: Input image as numpy array
prompt: Optional text prompt for video generation
selected_model: Selected WAN model variant
enabled_loras: List of active LoRA adapters
enable_nsfw: Whether to allow NSFW content generation
video_length: Target video length in frames
resolution: Output resolution preset
progress: Gradio progress tracker
Returns:
Tuple of (video_path, status_message, generation_metadata)
"""
try:
# Step 1: Validate inputs
progress(0.1, desc="🔍 Validating inputs...")
if image is None:
raise gr.Error("No image provided. Please upload an image to generate video.")
# Step 2: NSFW check if enabled
if enable_nsfw and NSFW_CONFIG["require_confirmation"]:
progress(0.15, desc="⚠️ NSFW mode active - bypassing standard filters")
elif not enable_nsfw:
progress(0.15, desc="🛡️ Running safety checks...")
if nsfw_checker.check_image(image):
raise gr.Error("Input image flagged by safety filter. Enable NSFW mode to bypass.")
# Step 3: Load selected model and LoRAs
progress(0.2, desc=f"📦 Loading {selected_model} model...")
generator.load_model(selected_model)
progress(0.3, desc=f"🔌 Activating {len(enabled_loras)} LoRA adapters...")
active_loras = lora_manager.load_loras(enabled_loras)
# Step 4: Generate video frames
progress(0.4, desc="🎬 Generating video frames...")
frames = []
for i in range(video_length):
progress(0.4 + (i / video_length) * 0.5,
desc=f"Rendering frame {i+1}/{video_length}...")
frame = generator.generate_frame(
image=image,
prompt=prompt,
frame_index=i,
total_frames=video_length,
active_loras=active_loras
)
frames.append(frame)
time.sleep(0.1) # Simulate processing time
# Step 5: Compile video
progress(0.95, desc="🎥 Compiling final video...")
output_path = generator.compile_video(
frames=frames,
resolution=resolution,
fps=30
)
# Step 6: Prepare metadata
metadata = {
"model": selected_model,
"loras": enabled_loras,
"nsfw_mode": enable_nsfw,
"resolution": resolution,
"frames": video_length,
"prompt": prompt or "No prompt provided",
"status": "✅ Generation complete"
}
progress(1.0, desc="✅ Done!")
return output_path, "Video generated successfully!", metadata
except Exception as e:
raise gr.Error(f"Generation failed: {str(e)}")
def update_lora_visibility(enable_nsfw: bool) -> Dict[str, Any]:
"""Update LoRA options based on NSFW mode"""
if enable_nsfw:
return gr.Dropdown(
choices=list(AVAILABLE_LORAS.keys()),
value=[],
multiselect=True,
label="🎨 Active LoRA Adapters (NSFW options unlocked)"
)
else:
safe_loras = {k: v for k, v in AVAILABLE_LORAS.items() if not v.get("nsfw", False)}
return gr.Dropdown(
choices=list(safe_loras.keys()),
value=[],
multiselect=True,
label="🎨 Active LoRA Adapters (Safe mode)"
)
def create_interface():
"""Create the main Gradio interface"""
with gr.Blocks() as demo:
gr.HTML("""
🥊 WAN-Scale Image-to-Video Architecture 🥊
Built with anycoder - View on Hugging Face
Turn static images into dynamic videos with WAN foundation models
""")
# Global state
generation_state = gr.State({"session_id": None})
with gr.Row():
# Sidebar for controls
with gr.Sidebar(position="left", width=320):
gr.Markdown("### ⚙️ Generation Settings")
model_selector = gr.Dropdown(
choices=list(MODEL_CONFIGS.keys()),
value="wan-2.1-14b",
label="🤖 WAN Model",
info="Select foundation model variant"
)
nsfw_toggle = gr.Checkbox(
value=False,
label="🔞 Enable NSFW Content",
info="Bypass safety filters (requires confirmation)"
)
lora_selector = gr.Dropdown(
choices=[k for k, v in AVAILABLE_LORAS.items() if not v.get("nsfw", False)],
value=[],
multiselect=True,
label="🎨 Active LoRA Adapters",
info="Select style and domain adapters"
)
with gr.Accordion("📐 Video Settings", open=False):
video_length = gr.Slider(
minimum=16,
maximum=128,
value=32,
step=8,
label="Video Length (frames)"
)
resolution = gr.Radio(
choices=["512x512", "768x768", "1024x576", "1920x1080"],
value="768x768",
label="Resolution"
)
with gr.Accordion("🚀 Advanced Options", open=False):
inference_steps = gr.Slider(
minimum=10,
maximum=100,
value=50,
label="Inference Steps"
)
cfg_scale = gr.Slider(
minimum=1.0,
maximum=20.0,
value=7.5,
step=0.5,
label="CFG Scale"
)
# Status indicators
model_status = gr.Label(
value={"Status": "Ready", "VRAM": "24GB Available"},
label="System Status"
)
# Main content area
with gr.Column():
gr.Markdown("### 📤 Input Image")
input_image = gr.Image(
label="Upload Starting Frame",
type="numpy",
height=400,
sources=["upload", "webcam", "clipboard"]
)
gr.Markdown("### 📝 Optional Text Prompt")
prompt_box = gr.Textbox(
placeholder="Describe the motion, style, or scene...",
label="Prompt (optional)",
lines=2,
max_lines=4
)
with gr.Row():
generate_btn = gr.Button(
"🎬 Generate Video",
variant="primary",
scale=2
)
clear_btn = gr.ClearButton(
components=[input_image, prompt_box],
value="🗑️ Clear"
)
# Progress tracking
progress_bar = gr.Progress()
status_text = gr.Textbox(
label="Status",
interactive=False,
show_copy_button=True
)
gr.Markdown("### 📼 Output Video")
output_video = gr.Video(
label="Generated Video",
height=400,
autoplay=True,
show_download_button=True
)
# Generation metadata
with gr.Accordion("📊 Generation Details", open=False):
metadata_json = gr.JSON(
label="Metadata",
open=False
)
# Event handlers
nsfw_toggle.change(
fn=update_lora_visibility,
inputs=nsfw_toggle,
outputs=lora_selector,
api_visibility="private"
)
generate_btn.click(
fn=generate_video,
inputs=[
input_image,
prompt_box,
model_selector,
lora_selector,
nsfw_toggle,
video_length,
resolution
],
outputs=[
output_video,
status_text,
metadata_json
],
api_visibility="public",
concurrency_limit=2 # Limit concurrent generations
)
# Update model status on selection
model_selector.change(
fn=lambda x: {"Status": f"Loaded {x}", "VRAM": "24GB Used"},
inputs=model_selector,
outputs=model_status,
api_visibility="private"
)
# Demo load event
demo.load(
fn=lambda: "System initialized and ready",
outputs=status_text,
api_visibility="private"
)
return demo
# Create and launch the application
if __name__ == "__main__":
demo = create_interface()
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=False,
show_error=True,
max_threads=4,
theme=gr.themes.Soft(
primary_hue="purple",
secondary_hue="indigo",
neutral_hue="slate",
font=gr.themes.GoogleFont("Inter"),
text_size="lg",
spacing_size="lg",
radius_size="md"
).set(
button_primary_background_fill="*primary_600",
button_primary_background_fill_hover="*primary_700",
block_title_text_weight="600",
block_background_fill="*neutral_50"
),
footer_links=[
{"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"},
{"label": "Model Docs", "url": "https://huggingface.co/docs"},
{"label": "API Reference", "url": "/docs"}
],
css="""
.gradio-container { max-width: 1400px; margin: auto; }
.contain { display: flex; flex-direction: column; height: 100vh; }
#component-0 { height: 100%; }
.gr-button { font-weight: 600; }
.gr-markdown { text-align: center; }
"""
)