File size: 11,850 Bytes
69f2bad
8e5115d
 
 
 
 
 
 
69f2bad
e632d6b
 
8e5115d
e632d6b
 
 
8e5115d
e632d6b
 
8e5115d
 
 
e632d6b
8e5115d
e632d6b
 
 
 
 
8e5115d
e632d6b
 
 
 
 
 
 
 
8e5115d
e632d6b
 
8e5115d
 
e632d6b
 
8e5115d
e632d6b
 
8e5115d
e632d6b
8e5115d
 
 
 
 
 
 
 
 
 
e632d6b
 
 
8e5115d
e632d6b
 
 
 
 
 
8e5115d
 
 
 
 
e632d6b
 
8e5115d
e632d6b
 
 
 
 
8e5115d
 
e632d6b
 
8e5115d
 
 
 
e632d6b
8e5115d
 
e632d6b
 
 
8e5115d
 
 
e632d6b
 
8e5115d
 
 
 
 
 
 
e632d6b
8e5115d
 
 
 
 
e632d6b
8e5115d
 
 
e632d6b
8e5115d
 
 
 
e632d6b
 
 
 
8e5115d
e632d6b
8e5115d
 
 
e632d6b
 
 
8e5115d
 
e632d6b
8e5115d
 
 
 
 
e632d6b
 
 
 
8e5115d
e632d6b
 
8e5115d
 
 
e632d6b
 
 
 
8e5115d
e632d6b
 
8e5115d
 
e632d6b
8e5115d
e632d6b
 
 
8e5115d
 
e632d6b
8e5115d
e632d6b
 
 
 
 
 
8e5115d
 
e632d6b
8e5115d
 
e632d6b
 
 
8e5115d
e632d6b
8e5115d
 
e632d6b
 
 
 
 
8e5115d
 
 
 
 
e632d6b
8e5115d
 
 
 
 
 
e632d6b
8e5115d
 
 
e632d6b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8e5115d
e632d6b
 
 
 
 
 
 
 
 
 
 
 
8e5115d
e632d6b
 
 
 
 
 
 
 
 
 
 
 
 
 
8e5115d
e632d6b
8e5115d
e632d6b
 
 
8e5115d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e632d6b
 
 
 
8e5115d
 
 
 
 
e632d6b
8e5115d
 
e632d6b
 
8e5115d
e632d6b
8e5115d
e632d6b
 
 
8e5115d
e632d6b
 
 
 
 
8e5115d
e632d6b
 
 
 
8e5115d
e632d6b
 
 
 
8e5115d
e632d6b
 
 
 
8e5115d
 
e632d6b
8e5115d
e632d6b
8e5115d
 
e632d6b
8e5115d
e632d6b
8e5115d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
import gradio as gr
import torch
import os
import gc
import numpy as np
import tempfile
from typing import Optional, Tuple
import time

# ZeroGPU import - bu çok önemli!
import spaces

# Check if running in ZeroGPU environment
IS_ZERO_GPU = os.environ.get("SPACES_ZERO_GPU") == "true"
IS_SPACES = os.environ.get("SPACE_ID") is not None

def load_model():
    """Load LTX-Video model - this will run on ZeroGPU when decorated"""
    try:
        from diffusers import LTXVideoPipeline
        
        print("🔄 Loading LTX-Video model...")
        
        pipe = LTXVideoPipeline.from_pretrained(
            "Lightricks/LTX-Video",
            torch_dtype=torch.bfloat16,
            use_safetensors=True,
        )
        
        # ZeroGPU optimizations
        if IS_ZERO_GPU:
            pipe = pipe.to("cuda")
            # Enable memory optimizations
            pipe.enable_vae_slicing()
            pipe.enable_vae_tiling()
            if hasattr(pipe, 'enable_memory_efficient_attention'):
                pipe.enable_memory_efficient_attention()
        
        print("✅ Model loaded successfully!")
        return pipe
        
    except Exception as e:
        print(f"❌ Model loading failed: {e}")
        return None

# Global model variable - will be loaded when needed
MODEL = None

@spaces.GPU(duration=120)  # ZeroGPU decorator - 2 dakika GPU kullanımı
def generate_video(
    prompt: str,
    negative_prompt: str = "",
    num_frames: int = 25,
    height: int = 512,
    width: int = 512,
    num_inference_steps: int = 20,
    guidance_scale: float = 7.5,
    seed: int = -1
) -> Tuple[Optional[str], str]:
    """Generate video using LTX-Video with ZeroGPU"""
    
    global MODEL
    
    # Load model if not already loaded
    if MODEL is None:
        MODEL = load_model()
    
    if MODEL is None:
        return None, "❌ Model loading failed. Please try again."
    
    # Input validation
    if not prompt.strip():
        return None, "❌ Please enter a valid prompt."
    
    if len(prompt) > 300:
        return None, "❌ Prompt too long. Please keep it under 300 characters."
    
    # ZeroGPU optimizations - limit parameters for stability
    num_frames = min(num_frames, 25)  # Max 25 frames
    num_inference_steps = min(num_inference_steps, 25)  # Max 25 steps
    height = min(height, 768)  # Max 768px
    width = min(width, 768)   # Max 768px
    
    try:
        # Clear CUDA cache
        torch.cuda.empty_cache()
        gc.collect()
        
        # Set seed for reproducibility
        generator = None
        if seed == -1:
            seed = np.random.randint(0, 2**32 - 1)
        
        generator = torch.Generator(device="cuda").manual_seed(seed)
        
        print(f"🎬 Generating video: {prompt}")
        start_time = time.time()
        
        # Generate video
        with torch.autocast("cuda", dtype=torch.bfloat16):
            result = MODEL(
                prompt=prompt,
                negative_prompt=negative_prompt if negative_prompt else None,
                num_frames=num_frames,
                height=height,
                width=width,
                num_inference_steps=num_inference_steps,
                guidance_scale=guidance_scale,
                generator=generator,
            )
        
        end_time = time.time()
        generation_time = end_time - start_time
        
        # Export video
        video_frames = result.frames[0]
        
        with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp_file:
            # Export to video file
            from diffusers.utils import export_to_video
            export_to_video(video_frames, tmp_file.name, fps=8)
            video_path = tmp_file.name
        
        # Clear memory
        torch.cuda.empty_cache()
        gc.collect()
        
        success_msg = f"""
        ✅ Video generated successfully with ZeroGPU!
        
        📝 Prompt: {prompt}
        🎬 Frames: {num_frames}
        📐 Resolution: {width}x{height}  
        ⚙️ Inference Steps: {num_inference_steps}
        🎯 Guidance Scale: {guidance_scale}
        🎲 Seed: {seed}
        ⏱️ Generation Time: {generation_time:.1f}s
        🖥️ ZeroGPU: {'✅' if IS_ZERO_GPU else '❌'}
        """
        
        return video_path, success_msg
        
    except torch.cuda.OutOfMemoryError:
        torch.cuda.empty_cache()
        gc.collect()
        return None, "❌ GPU memory exceeded. Try reducing frames, resolution, or inference steps."
    
    except Exception as e:
        torch.cuda.empty_cache()
        gc.collect()
        return None, f"❌ Generation failed: {str(e)}"

def get_system_info():
    """Get system information"""
    gpu_info = "Not available"
    if torch.cuda.is_available():
        gpu_info = f"{torch.cuda.get_device_name(0)} ({torch.cuda.get_device_properties(0).total_memory / 1024**3:.1f} GB)"
    
    return f"""
    ## 🖥️ System Information
    
    **Environment:**
    - ZeroGPU: {'✅ Active' if IS_ZERO_GPU else '❌ Not detected'}
    - Hugging Face Spaces: {'✅' if IS_SPACES else '❌'}
    - CUDA Available: {'✅' if torch.cuda.is_available() else '❌'}
    - GPU: {gpu_info}
    - PyTorch: {torch.__version__}
    
    **Model Status:**
    - LTX-Video: {'✅ Loaded' if MODEL is not None else '⏳ Will load on first use'}
    
    **ZeroGPU Benefits:**
    - ✅ Free GPU access
    - ✅ A100 40GB GPU
    - ✅ Automatic resource management
    - ⏱️ 120 second timeout per generation
    """

# Create Gradio interface
with gr.Blocks(title="LTX-Video with ZeroGPU", theme=gr.themes.Soft()) as demo:
    
    gr.Markdown("""
    # 🚀 LTX-Video Generator (ZeroGPU Powered)
    
    Generate high-quality videos from text using Lightricks' LTX-Video model, powered by **ZeroGPU**!
    
    ⚡ **Free GPU access** - No need to upgrade your Space hardware!
    """)
    
    if IS_ZERO_GPU:
        gr.Markdown("✅ **ZeroGPU Active** - You have free access to A100 GPU!")
    else:
        gr.Markdown("⚠️ **ZeroGPU not detected** - Make sure you've enabled ZeroGPU in your Space settings.")
    
    with gr.Tab("🎥 Generate Video"):
        with gr.Row():
            with gr.Column(scale=1):
                prompt_input = gr.Textbox(
                    label="📝 Video Prompt",
                    placeholder="A serene mountain lake reflecting the aurora borealis...",
                    lines=3,
                    max_lines=5
                )
                
                negative_prompt_input = gr.Textbox(
                    label="🚫 Negative Prompt (Optional)",
                    placeholder="blurry, low quality, distorted, text, watermark...",
                    lines=2
                )
                
                with gr.Accordion("🔧 Advanced Settings", open=False):
                    with gr.Row():
                        num_frames = gr.Slider(
                            minimum=8,
                            maximum=25,  # Limited for ZeroGPU
                            value=16,
                            step=1,
                            label="🎬 Number of Frames"
                        )
                        
                        num_steps = gr.Slider(
                            minimum=10,
                            maximum=25,  # Limited for ZeroGPU  
                            value=20,
                            step=1,
                            label="⚙️ Inference Steps"
                        )
                    
                    with gr.Row():
                        width = gr.Dropdown(
                            choices=[256, 512, 768],  # Limited for ZeroGPU
                            value=512,
                            label="📐 Width"
                        )
                        
                        height = gr.Dropdown(
                            choices=[256, 512, 768],  # Limited for ZeroGPU
                            value=512,
                            label="📏 Height"
                        )
                    
                    with gr.Row():
                        guidance_scale = gr.Slider(
                            minimum=1.0,
                            maximum=15.0,
                            value=7.5,
                            step=0.5,
                            label="🎯 Guidance Scale"
                        )
                        
                        seed = gr.Number(
                            label="🎲 Seed (-1 for random)",
                            value=-1,
                            precision=0
                        )
                
                generate_btn = gr.Button("🚀 Generate Video with ZeroGPU", variant="primary", size="lg")
                
                gr.Markdown("""
                **⏱️ Note:** Each generation uses 2 minutes of ZeroGPU time.
                """)
                
            with gr.Column(scale=1):
                video_output = gr.Video(
                    label="🎥 Generated Video",
                    height=400
                )
                
                result_text = gr.Textbox(
                    label="📋 Generation Info",
                    lines=8,
                    show_copy_button=True
                )
        
        # Event handler
        generate_btn.click(
            fn=generate_video,
            inputs=[
                prompt_input, negative_prompt_input, num_frames,
                height, width, num_steps, guidance_scale, seed
            ],
            outputs=[video_output, result_text]
        )
        
        # Example prompts
        gr.Examples(
            examples=[
                ["A majestic eagle soaring over snow-capped mountains", "blurry, low quality", 16, 512, 512, 20, 7.5, 42],
                ["Ocean waves gently lapping on a tropical beach at sunset", "", 20, 512, 512, 20, 8.0, 123],
                ["A steaming cup of coffee on a rainy window sill", "text, watermark", 16, 512, 512, 15, 7.0, 456],
                ["Cherry blossoms falling in a peaceful Japanese garden", "", 20, 768, 512, 20, 7.5, 789]
            ],
            inputs=[prompt_input, negative_prompt_input, num_frames, height, width, num_steps, guidance_scale, seed]
        )
    
    with gr.Tab("ℹ️ System Info"):
        info_btn = gr.Button("🔍 Check System Status", variant="secondary")
        system_output = gr.Markdown()
        
        info_btn.click(fn=get_system_info, outputs=system_output)
        demo.load(fn=get_system_info, outputs=system_output)
    
    with gr.Tab("📚 ZeroGPU Guide"):
        gr.Markdown("""
        ## 🚀 ZeroGPU Nedir?
        
        **ZeroGPU**, Hugging Face'in ücretsiz GPU hizmetidir:
        
        ### ✅ Avantajları:
        - **Ücretsiz A100 GPU** erişimi
        - **40GB GPU belleği** 
        - Otomatik kaynak yönetimi
        - CPU Basic Space'te bile çalışır
        
        ### ⚙️ Nasıl Etkinleştirilir:
        1. Space Settings → Advanced → ZeroGPU etkinleştir
        2. `requirements.txt`'e `spaces` ekle  
        3. Kodda `@spaces.GPU()` decorator kullan
        
        ### 📊 Limitler:
        - Fonksiyon başına max 120 saniye
        - Eşzamanlı kullanım sınırı
        - Yoğun zamanlarda kuyruk
        
        ### 💡 İpuçları:
        - Küçük parametrelerle başlayın
        - İlk çalıştırma model yükleme nedeniyle uzun sürebilir
        - Hata alırsanız birkaç saniye bekleyip tekrar deneyin
        """)

# Launch the app
if __name__ == "__main__":
    demo.queue(max_size=10)  # ZeroGPU için queue gerekli
    demo.launch(
        share=False,
        server_name="0.0.0.0", 
        server_port=7860,
        show_error=True
    )