File size: 26,348 Bytes
0b4562b
 
 
 
 
 
 
1ac6ba4
0b4562b
1ac6ba4
 
 
3a6a9cd
 
1ac6ba4
84463ec
0b4562b
 
 
 
 
 
84463ec
 
 
447bd94
 
 
 
 
 
 
 
0b4562b
 
 
3edcad7
 
 
 
447bd94
 
42a331b
 
 
447bd94
 
 
 
86fdec4
447bd94
 
86fdec4
 
 
 
 
 
 
 
 
447bd94
 
 
 
 
 
 
 
4d5b25f
 
 
 
02a442c
 
 
 
 
 
 
 
 
 
3216b1d
 
02a442c
 
bd6dbaf
 
3216b1d
bd6dbaf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3216b1d
 
02a442c
 
447bd94
 
fcc18a6
 
 
 
447bd94
 
 
 
0b4562b
 
 
 
 
dc2ca3c
 
0b4562b
 
dc2ca3c
0b4562b
 
 
447bd94
86fdec4
 
 
 
 
 
 
447bd94
 
 
 
 
 
 
 
 
0b4562b
447bd94
fcc18a6
 
86fdec4
fcc18a6
0b4562b
86fdec4
0b4562b
 
 
 
fcc18a6
bd6dbaf
 
 
 
 
fcc18a6
0b4562b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91ce5f5
 
0b4562b
 
bd6dbaf
 
fcc18a6
0ce71e8
 
 
 
3216b1d
0ce71e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b4562b
 
fcc18a6
0ce71e8
 
fcc18a6
 
 
0b4562b
 
 
3a6a9cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b4562b
3a6a9cd
0b4562b
3a6a9cd
fcc18a6
0b4562b
3a6a9cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0ce71e8
3a6a9cd
0ce71e8
3a6a9cd
0ce71e8
0b4562b
 
 
 
 
 
 
86fdec4
 
 
 
 
447bd94
 
 
 
 
 
 
 
 
 
0b4562b
447bd94
0b4562b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3421c13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15008bf
 
 
 
 
 
 
 
 
 
0b4562b
15008bf
 
 
 
 
 
0b4562b
 
ff7da2f
 
0b4562b
ff7da2f
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
0e6f557
 
ff7da2f
 
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
06f3d78
ff7da2f
 
 
 
 
 
 
0b4562b
ff7da2f
 
 
 
 
 
0b4562b
ff7da2f
 
 
 
 
 
 
 
0b4562b
ff7da2f
 
 
 
 
 
 
0b4562b
 
 
 
 
 
9e01ee0
 
0b4562b
 
ff7da2f
0b4562b
ff7da2f
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
0e6f557
 
ff7da2f
 
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
06f3d78
ff7da2f
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
 
 
06f3d78
ff7da2f
 
 
 
 
 
 
0b4562b
ff7da2f
 
 
 
 
 
0b4562b
ff7da2f
 
 
 
 
 
 
0b4562b
ff7da2f
 
 
 
 
 
 
0b4562b
 
 
 
 
 
 
9e01ee0
 
0b4562b
 
 
1dfb088
 
 
7f796c3
 
 
 
 
 
0194c79
1dfb088
 
7f796c3
0b4562b
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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
"""
Hugging Face Space for STARFlow
Text-to-Image and Text-to-Video Generation

This app allows you to run STARFlow inference on Hugging Face GPU infrastructure.
"""

# Fix OpenMP warning - MUST be set BEFORE importing torch
import os
os.environ['OMP_NUM_THREADS'] = '1'
os.environ['MKL_NUM_THREADS'] = '1'
os.environ['NUMEXPR_NUM_THREADS'] = '1'
# Fix CUDA memory fragmentation
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'

import warnings
import gradio as gr
import torch
import subprocess
import pathlib
from pathlib import Path

# Suppress harmless warnings
warnings.filterwarnings("ignore", category=FutureWarning, message=".*torch.distributed.reduce_op.*")

# Try to import huggingface_hub for downloading checkpoints
try:
    from huggingface_hub import hf_hub_download
    HF_HUB_AVAILABLE = True
except ImportError:
    HF_HUB_AVAILABLE = False
    print("⚠️  huggingface_hub not available. Install with: pip install huggingface_hub")

# Check if running on Hugging Face Spaces
HF_SPACE = os.environ.get("SPACE_ID") is not None

# Default checkpoint paths (if uploaded to Space Files)
DEFAULT_IMAGE_CHECKPOINT = "ckpts/starflow_3B_t2i_256x256.pth"
DEFAULT_VIDEO_CHECKPOINT = "ckpts/starflow-v_7B_t2v_caus_480p_v3.pth"

# Model Hub repositories (if using Hugging Face Model Hub)
# Set these to your Model Hub repo IDs if you upload checkpoints there
# Format: "username/repo-name"
IMAGE_CHECKPOINT_REPO = "GlobalStudio/starflow-3b-checkpoint"  # Update this after creating Model Hub repo
VIDEO_CHECKPOINT_REPO = "GlobalStudio/starflow-v-7b-checkpoint"  # Update this after creating Model Hub repo

def get_checkpoint_path(checkpoint_file, default_local_path, repo_id=None, filename=None):
    """Get checkpoint path, downloading from Hub if needed."""
    # If user uploaded a file, use it
    if checkpoint_file is not None and checkpoint_file != "":
        if hasattr(checkpoint_file, 'name'):
            return checkpoint_file.name
        checkpoint_str = str(checkpoint_file)
        # If it's a file path that exists, use it
        if os.path.exists(checkpoint_str):
            return checkpoint_str
        # If it's the default path but doesn't exist, continue to download
        if checkpoint_str == default_local_path and not os.path.exists(checkpoint_str):
            pass  # Continue to download logic below
        elif checkpoint_str != default_local_path:
            return checkpoint_str
    
    # Try local path first
    if os.path.exists(default_local_path):
        return default_local_path
    
    # Try downloading from Model Hub if configured
    if repo_id and filename and HF_HUB_AVAILABLE:
        try:
            # Use /workspace if available (persistent), otherwise /tmp
            cache_dir = "/workspace/checkpoints" if os.path.exists("/workspace") else "/tmp/checkpoints"
            os.makedirs(cache_dir, exist_ok=True)
            
            # Check if already downloaded
            possible_path = os.path.join(cache_dir, "models--" + repo_id.replace("/", "--"), "snapshots", "*", filename)
            import glob
            existing = glob.glob(possible_path)
            if existing:
                checkpoint_path = existing[0]
                print(f"βœ… Using cached checkpoint: {checkpoint_path}")
                return checkpoint_path
            
            # Download with progress tracking
            import time
            start_time = time.time()
            print(f"πŸ“₯ Downloading checkpoint from {repo_id}...")
            print(f"File size: ~15.5 GB - This may take 10-30 minutes")
            print(f"Cache directory: {cache_dir}")
            print(f"Progress will be shown below...")
            
            # Use tqdm for progress if available
            try:
                from tqdm import tqdm
                from huggingface_hub.utils import tqdm as hf_tqdm
                checkpoint_path = hf_hub_download(
                    repo_id=repo_id,
                    filename=filename,
                    cache_dir=cache_dir,
                    local_files_only=False,
                    resume_download=True,  # Resume if interrupted
                )
            except Exception as e:
                # Fallback if tqdm fails
                print(f"Note: Progress bar unavailable, downloading silently...")
                checkpoint_path = hf_hub_download(
                    repo_id=repo_id,
                    filename=filename,
                    cache_dir=cache_dir,
                    local_files_only=False,
                    resume_download=True,
                )
            
            elapsed = time.time() - start_time
            print(f"βœ… Download completed in {elapsed/60:.1f} minutes")
            print(f"βœ… Checkpoint at: {checkpoint_path}")
            return checkpoint_path
        except Exception as e:
            error_detail = str(e)
            if "404" in error_detail or "not found" in error_detail.lower():
                return None, f"Checkpoint not found in Model Hub.\n\nPlease verify:\n1. Repo exists: https://huggingface.co/{repo_id}\n2. File exists: {filename}\n3. Repo is Public (not Private)\n\nError: {error_detail}"
            return None, f"Error downloading checkpoint: {error_detail}\n\nThis may take 10-30 minutes for a 14GB file. Please wait or check your internet connection."
    
    # No checkpoint found
    return None, f"Checkpoint not found. Please upload a checkpoint file or configure Model Hub repository."

# Verify CUDA availability (will be True on HF Spaces with GPU hardware)
if torch.cuda.is_available():
    print(f"βœ… CUDA available! Device: {torch.cuda.get_device_name(0)}")
    print(f"   CUDA Version: {torch.version.cuda}")
    print(f"   PyTorch Version: {torch.__version__}")
    print(f"   GPU Count: {torch.cuda.device_count()}")
    print(f"   Current Device: {torch.cuda.current_device()}")
else:
    print("⚠️  CUDA not available. Make sure GPU hardware is selected in Space settings.")
    print(f"   PyTorch Version: {torch.__version__}")

def generate_image(prompt, aspect_ratio, cfg, seed, checkpoint_file, config_path):
    """Generate image from text prompt."""
    # Get checkpoint path (from upload, local, or Model Hub)
    status_msg = ""
    
    # Handle checkpoint file (might be string from hidden Textbox)
    if checkpoint_file == DEFAULT_IMAGE_CHECKPOINT or checkpoint_file == "" or checkpoint_file is None:
        # Use Model Hub download
        checkpoint_file = None
    
    result = get_checkpoint_path(
        checkpoint_file, 
        DEFAULT_IMAGE_CHECKPOINT,
        IMAGE_CHECKPOINT_REPO,
        "starflow_3B_t2i_256x256.pth"
    )
    
    if isinstance(result, tuple) and result[0] is None:
        return None, result[1]  # Error message
    
    checkpoint_path = result
    
    # Show status
    status_msg += f"Using checkpoint: {checkpoint_path}\n"
    
    if not os.path.exists(checkpoint_path):
        return None, f"Error: Checkpoint file not found at {checkpoint_path}.\n\nPlease verify:\n1. Model Hub repo exists: {IMAGE_CHECKPOINT_REPO}\n2. File name matches: starflow_3B_t2i_256x256.pth\n3. Repo is Public (not Private)\n\nAttempting to download from Model Hub..."
    
    if not config_path or not os.path.exists(config_path):
        return None, "Error: Config file not found. Please ensure config file exists."
    
    status_msg += "Starting image generation...\n"
    status_msg += "⏱️  Timing breakdown:\n"
    status_msg += "   - Checkpoint download: 10-30 min (first time only, ~15.5 GB)\n"
    status_msg += "   - Model loading: 2-5 min (first time only)\n"
    status_msg += "   - Image generation: 1-3 min\n"
    status_msg += "   - Subsequent runs: Only generation time (~1-3 min)\n"
    
    try:
        # Create output directory
        output_dir = Path("outputs")
        output_dir.mkdir(exist_ok=True)
        
        # Run sampling command
        cmd = [
            "python", "sample.py",
            "--model_config_path", config_path,
            "--checkpoint_path", checkpoint_path,
            "--caption", prompt,
            "--sample_batch_size", "1",
            "--cfg", str(cfg),
            "--aspect_ratio", aspect_ratio,
            "--seed", str(seed),
            "--save_folder", "1",
            "--finetuned_vae", "none",
            "--jacobi", "1",
            "--jacobi_th", "0.001",
            "--jacobi_block_size", "16",
            "--logdir", str(output_dir)  # Set logdir to outputs directory
        ]
        
        status_msg += "πŸš€ Running generation...\n"
        status_msg += "πŸ“Š Current step: Model inference (checkpoint should already be downloaded)\n"
        
        # Create log file for debugging
        log_file = output_dir / "generation.log"
        status_msg += f"πŸ“‹ Logs will be saved to: {log_file}\n"
        
        # Run with timeout (45 minutes max - allows for download + generation)
        # Capture output and write to log file
        result = subprocess.run(
            cmd, 
            capture_output=True, 
            text=True, 
            cwd=os.getcwd(), 
            timeout=2700
        )
        
        # Write to log file
        with open(log_file, 'w') as log:
            log.write("=== GENERATION LOG ===\n\n")
            log.write(f"Command: {' '.join(cmd)}\n\n")
            log.write("=== STDOUT ===\n")
            log.write(result.stdout)
            log.write("\n\n=== STDERR ===\n")
            log.write(result.stderr)
            log.write(f"\n\n=== RETURN CODE: {result.returncode} ===\n")
        
        # Read log file for detailed output
        log_content = ""
        if log_file.exists():
            with open(log_file, 'r') as f:
                log_content = f.read()
            status_msg += f"\nπŸ“‹ Full logs available at: {log_file}\n"
        
        if result.returncode != 0:
            error_msg = f"Error during generation:\n{result.stderr}\n\nStdout:\n{result.stdout}"
            if log_content:
                error_msg += f"\n\nπŸ“‹ Full log file ({log_file}):\n{log_content[-2000:]}"  # Last 2000 chars
            return None, error_msg
        
        status_msg += "Generation complete. Looking for output...\n"
        
        # Find the generated image
        # The sample.py script saves to logdir/model_name/...
        # Model name is derived from checkpoint path stem
        checkpoint_stem = Path(checkpoint_path).stem
        model_output_dir = output_dir / checkpoint_stem
        
        status_msg += f"Searching in: {model_output_dir}\n"
        status_msg += f"Also searching recursively in: {output_dir}\n"
        
        # Search in model-specific directory first, then recursively
        search_paths = [model_output_dir, output_dir]
        output_files = []
        
        for search_path in search_paths:
            if search_path.exists():
                # Look for PNG, JPG, JPEG files
                found = list(search_path.glob("**/*.png")) + list(search_path.glob("**/*.jpg")) + list(search_path.glob("**/*.jpeg"))
                output_files.extend(found)
                status_msg += f"Found {len(found)} files in {search_path}\n"
        
        if output_files:
            # Get the most recent file
            latest_file = max(output_files, key=lambda p: p.stat().st_mtime)
            status_msg += f"βœ… Found image: {latest_file}\n"
            return str(latest_file), status_msg + "βœ… Success! Image generated."
        else:
            # Debug: list what's actually in the directory
            debug_info = f"\n\nDebug info:\n"
            debug_info += f"Output dir exists: {output_dir.exists()}\n"
            if output_dir.exists():
                debug_info += f"Contents of {output_dir}:\n"
                for item in output_dir.iterdir():
                    debug_info += f"  - {item.name} ({'dir' if item.is_dir() else 'file'})\n"
                if model_output_dir.exists():
                    debug_info += f"\nContents of {model_output_dir}:\n"
                    for item in model_output_dir.iterdir():
                        debug_info += f"  - {item.name} ({'dir' if item.is_dir() else 'file'})\n"
            
            error_msg = status_msg + f"Error: Generated image not found.\n"
            error_msg += f"Searched in: {output_dir} and {model_output_dir}\n"
            error_msg += debug_info
            if log_content:
                error_msg += f"\n\nπŸ“‹ Check log file for details: {log_file}\nLast 2000 chars:\n{log_content[-2000:]}"
            else:
                error_msg += f"\n\nCheck stdout:\n{result.stdout[-1000:]}"
            return None, error_msg
            
    except Exception as e:
        return None, f"Error: {str(e)}"


def generate_video(prompt, aspect_ratio, cfg, seed, target_length, checkpoint_file, config_path, input_image):
    """Generate video from text prompt."""
    # Handle checkpoint file (might be string from hidden Textbox)
    if checkpoint_file == DEFAULT_VIDEO_CHECKPOINT or checkpoint_file == "" or checkpoint_file is None:
        # Use Model Hub download
        checkpoint_file = None
    
    # Get checkpoint path (from upload, local, or Model Hub)
    result = get_checkpoint_path(
        checkpoint_file,
        DEFAULT_VIDEO_CHECKPOINT,
        VIDEO_CHECKPOINT_REPO,
        "starflow-v_7B_t2v_caus_480p_v3.pth"
    )
    
    if isinstance(result, tuple) and result[0] is None:
        return None, result[1]  # Error message
    
    checkpoint_path = result
    if not os.path.exists(checkpoint_path):
        return None, f"Error: Checkpoint file not found at {checkpoint_path}."
    
    if not config_path or not os.path.exists(config_path):
        return None, "Error: Config file not found. Please ensure config file exists."
    
    # Handle input image
    input_image_path = None
    if input_image is not None:
        if hasattr(input_image, 'name'):
            input_image_path = input_image.name
        else:
            input_image_path = str(input_image)
    
    try:
        # Create output directory
        output_dir = Path("outputs")
        output_dir.mkdir(exist_ok=True)
        
        # Run sampling command
        cmd = [
            "python", "sample.py",
            "--model_config_path", config_path,
            "--checkpoint_path", checkpoint_path,
            "--caption", prompt,
            "--sample_batch_size", "1",
            "--cfg", str(cfg),
            "--aspect_ratio", aspect_ratio,
            "--seed", str(seed),
            "--out_fps", "16",
            "--save_folder", "1",
            "--jacobi", "1",
            "--jacobi_th", "0.001",
            "--finetuned_vae", "none",
            "--disable_learnable_denoiser", "0",
            "--jacobi_block_size", "32",
            "--target_length", str(target_length)
        ]
        
        if input_image_path and os.path.exists(input_image_path):
            cmd.extend(["--input_image", input_image_path])
        else:
            cmd.extend(["--input_image", "none"])
        
        result = subprocess.run(cmd, capture_output=True, text=True, cwd=os.getcwd())
        
        if result.returncode != 0:
            return None, f"Error: {result.stderr}"
        
        # Find the generated video
        output_files = list(output_dir.glob("**/*.mp4")) + list(output_dir.glob("**/*.gif"))
        if output_files:
            latest_file = max(output_files, key=lambda p: p.stat().st_mtime)
            return str(latest_file), "Success! Video generated."
        else:
            return None, "Error: Generated video not found."
            
    except Exception as e:
        return None, f"Error: {str(e)}"


# Create Gradio interface
with gr.Blocks(title="STARFlow - Text-to-Image & Video Generation") as demo:
    # Add custom CSS using gr.HTML
    gr.HTML("""
    <style>
        .gradio-container {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
        }
        .main-header {
            text-align: center;
            padding: 2rem 0;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border-radius: 10px;
            margin-bottom: 2rem;
        }
        .main-header h1 {
            margin: 0;
            font-size: 2.5rem;
            font-weight: 700;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
        }
        .main-header p {
            margin: 0.5rem 0 0 0;
            font-size: 1.1rem;
            opacity: 0.95;
        }
        .info-box {
            background: #f0f4ff;
            border-left: 4px solid #667eea;
            padding: 1rem;
            border-radius: 5px;
            margin-bottom: 1.5rem;
        }
        .input-section {
            background: #fafafa;
            padding: 1.5rem;
            border-radius: 10px;
            border: 1px solid #e0e0e0;
        }
        .output-section {
            background: white;
            padding: 1.5rem;
            border-radius: 10px;
            border: 1px solid #e0e0e0;
            box-shadow: 0 2px 8px rgba(0,0,0,0.05);
        }
        .generate-btn {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
            color: white !important;
            font-weight: 600 !important;
            padding: 0.75rem 2rem !important;
            border-radius: 8px !important;
            border: none !important;
            font-size: 1rem !important;
            transition: transform 0.2s, box-shadow 0.2s !important;
        }
        .generate-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4) !important;
        }
        .status-box {
            font-family: 'Monaco', 'Menlo', monospace;
            font-size: 0.9rem;
            line-height: 1.6;
        }
    </style>
    """)
    # Header
    gr.HTML("""
    <div class="main-header">
        <h1>🎨 STARFlow</h1>
        <p>Scalable Transformer Auto-Regressive Flow</p>
        <p style="font-size: 0.95rem; margin-top: 0.5rem; opacity: 0.9;">
            Generate high-quality images and videos from text prompts
        </p>
    </div>
    """)
    
    # Info box
    gr.Markdown("""
    <div class="info-box">
        <strong>ℹ️ Note:</strong> Checkpoints are automatically downloaded from Model Hub on first use. 
        First generation may take 10-20 minutes for download and model loading.
    </div>
    """)
    
    with gr.Tabs() as tabs:
        with gr.Tab("πŸ–ΌοΈ Text-to-Image", id="image_tab"):
            with gr.Row():
                with gr.Column(scale=1, min_width=400):
                    gr.Markdown("### βš™οΈ Generation Settings")
                    with gr.Group():
                        image_prompt = gr.Textbox(
                            label="πŸ“ Prompt",
                            placeholder="a film still of a cat playing piano",
                            lines=4
                        )
                        image_config = gr.Textbox(
                            label="βš™οΈ Config Path",
                            value="configs/starflow_3B_t2i_256x256.yaml",
                            interactive=False,
                            visible=False  # Hidden - not needed for users
                        )
                    
                    with gr.Group():
                        gr.Markdown("#### 🎨 Image Settings")
                        image_aspect = gr.Dropdown(
                            label="Aspect Ratio",
                            choices=["1:1", "2:3", "3:2", "16:9", "9:16", "4:5", "5:4"],
                            value="1:1"
                        )
                        image_cfg = gr.Slider(
                            label="CFG Scale",
                            minimum=1.0,
                            maximum=10.0,
                            value=3.6,
                            step=0.1
                        )
                        image_seed = gr.Number(
                            value=999,
                            label="🎲 Seed",
                            precision=0
                        )
                    
                    # Hidden checkpoint field
                    image_checkpoint = gr.Textbox(
                        label="Model Checkpoint Path (auto-downloaded)",
                        value=DEFAULT_IMAGE_CHECKPOINT,
                        visible=False
                    )
                    
                    image_btn = gr.Button(
                        "✨ Generate Image",
                        variant="primary",
                        size="lg",
                        elem_classes="generate-btn"
                    )
                
                with gr.Column(scale=1, min_width=500):
                    gr.Markdown("### 🎨 Generated Image")
                    image_output = gr.Image(
                        label="",
                        type="filepath",
                        height=500,
                        show_label=False
                    )
                    image_status = gr.Textbox(
                        label="πŸ“Š Status",
                        lines=12,
                        max_lines=20,
                        interactive=False,
                        elem_classes="status-box",
                        placeholder="Status messages will appear here..."
                    )
            
            image_btn.click(
                fn=generate_image,
                inputs=[image_prompt, image_aspect, image_cfg, image_seed, image_checkpoint, image_config],
                outputs=[image_output, image_status],
                show_progress=True,
                queue=True  # Use queue to handle long-running operations
            )
        
        with gr.Tab("🎬 Text-to-Video", id="video_tab"):
            with gr.Row():
                with gr.Column(scale=1, min_width=400):
                    gr.Markdown("### βš™οΈ Generation Settings")
                    with gr.Group():
                        video_prompt = gr.Textbox(
                            label="πŸ“ Prompt",
                            placeholder="a corgi dog looks at the camera",
                            lines=4
                        )
                        video_config = gr.Textbox(
                            label="βš™οΈ Config Path",
                            value="configs/starflow-v_7B_t2v_caus_480p.yaml",
                            interactive=False,
                            visible=False  # Hidden - not needed for users
                        )
                    
                    with gr.Group():
                        gr.Markdown("#### 🎬 Video Settings")
                        video_aspect = gr.Dropdown(
                            label="Aspect Ratio",
                            choices=["16:9", "1:1", "4:3"],
                            value="16:9"
                        )
                        video_cfg = gr.Slider(
                            label="CFG Scale",
                            minimum=1.0,
                            maximum=10.0,
                            value=3.5,
                            step=0.1
                        )
                        video_seed = gr.Number(
                            value=99,
                            label="🎲 Seed",
                            precision=0
                        )
                        video_length = gr.Slider(
                            label="Target Length (frames)",
                            minimum=81,
                            maximum=481,
                            value=81,
                            step=80
                        )
                    
                    with gr.Group():
                        gr.Markdown("#### πŸ–ΌοΈ Optional Input")
                        video_input_image = gr.Image(
                            label="Input Image (optional)",
                            type="filepath"
                        )
                    
                    # Hidden checkpoint field
                    video_checkpoint = gr.Textbox(
                        label="Model Checkpoint Path (auto-downloaded)",
                        value=DEFAULT_VIDEO_CHECKPOINT,
                        visible=False
                    )
                    
                    video_btn = gr.Button(
                        "✨ Generate Video",
                        variant="primary",
                        size="lg",
                        elem_classes="generate-btn"
                    )
                
                with gr.Column(scale=1, min_width=500):
                    gr.Markdown("### 🎬 Generated Video")
                    video_output = gr.Video(
                        label="",
                        height=500,
                        show_label=False
                    )
                    video_status = gr.Textbox(
                        label="πŸ“Š Status",
                        lines=12,
                        max_lines=20,
                        interactive=False,
                        elem_classes="status-box",
                        placeholder="Status messages will appear here..."
                    )
            
            video_btn.click(
                fn=generate_video,
                inputs=[video_prompt, video_aspect, video_cfg, video_seed, video_length, 
                       video_checkpoint, video_config, video_input_image],
                outputs=[video_output, video_status],
                show_progress=True,
                queue=True  # Use queue to handle long-running operations
            )

if __name__ == "__main__":
    # Enable queue for long-running operations
    demo.queue(default_concurrency_limit=1)
    
    # Password protection - users don't need HF accounts!
    # Change these to your desired username/password
    # For multiple users, use: auth=[("user1", "pass1"), ("user2", "pass2")]
    demo.launch(
        server_name="0.0.0.0",
        server_port=7860,
        auth=("starflow", "im30"),  # Change password!
        share=False,  # Set to True if you want public Gradio link
        max_threads=1  # Limit threads for stability
    )