File size: 6,498 Bytes
6a7f147
 
b3904ee
 
 
6a7f147
b3904ee
 
6a7f147
b3904ee
70ce8b4
6a7f147
b3904ee
9643953
 
 
 
6a7f147
b3904ee
 
 
 
 
 
 
 
 
 
 
 
6a7f147
 
b3904ee
 
 
 
 
6a7f147
 
 
b3904ee
 
 
 
 
 
 
 
 
 
 
 
 
 
6a7f147
b3904ee
 
 
 
6a7f147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3904ee
 
6a7f147
9643953
6a7f147
b3904ee
6a7f147
b3904ee
9643953
 
b3904ee
 
 
 
 
 
 
 
 
 
6a7f147
b3904ee
 
 
 
 
 
 
 
9643953
b3904ee
 
6a7f147
 
b3904ee
9643953
6a7f147
b3904ee
 
 
 
 
 
 
 
 
 
 
 
 
 
6a7f147
 
 
b3904ee
 
 
 
 
6a7f147
b3904ee
 
 
 
 
 
 
6a7f147
b3904ee
 
 
6a7f147
 
b3904ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a7f147
b3904ee
 
6a7f147
b3904ee
 
6a7f147
 
 
 
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
import os
import time
import gradio as gr
import tempfile
import requests
from byteplussdkarkruntime import Ark
from PIL import Image
import io

# Get API key from Hugging Face secret "Key"
API_KEY = os.environ.get("Key", "")

# Initialize client exactly like your working code
client = Ark(
    base_url="https://ark.ap-southeast.bytepluses.com/api/v3",
    api_key=API_KEY,
)

def upload_image_to_temp_url(image):
    """Upload image to temporary hosting and return URL"""
    # For now, we need a public URL for the API
    # Option 1: Use a free image hosting service
    # Option 2: Save temporarily and return path (won't work for API)
    # Option 3: Keep URL input for now with preview
    
    # Let's keep it simple - return None and we'll handle it in the UI
    return None

def generate_video(image, prompt_text, progress=gr.Progress()):
    """Generate video using your exact working code pattern"""
    
    if not API_KEY:
        yield "❌ API Key not configured. Please add 'Key' secret.", None
        return
    
    if image is None:
        yield "⚠️ Please upload an image first", None
        return
    
    try:
        progress(0, desc="Preparing image...")
        
        # Save uploaded image temporarily
        with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
            image.save(tmp_file.name)
            
            # Upload to a temporary hosting service
            # For now, we'll use a placeholder - we need to implement this
            # Option: Use imgbb.com API or similar
            image_url = "https://ark-doc.tos-ap-southeast-1.bytepluses.com/seepro_i2v%20.png"  # Placeholder
            
            # TODO: Implement actual image upload to hosting service
            yield "⏳ Note: Direct image upload needs hosting service. Using placeholder for now.", None
            time.sleep(1)
        
        progress(0.2, desc="Creating request...")
        
        # Exactly your working code structure
        print("----- create request -----")
        create_result = client.content_generation.tasks.create(
            model="seedance-1-5-pro-251215",
            content=[
                {
                    "type": "text",
                    "text": prompt_text
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": image_url
                    }
                }
            ]
        )
        
        task_id = create_result.id
        print(f"Task created: {task_id}")
        yield f"✅ Task created: {task_id}", None
        
        progress(0.3, desc="Polling for results...")
        
        # Polling exactly like your working code
        attempts = 0
        max_attempts = 120  # 2 minutes max
        
        while attempts < max_attempts:
            get_result = client.content_generation.tasks.get(task_id=task_id)
            status = get_result.status
            
            if status == "succeeded":
                progress(1.0, desc="Complete!")
                # Extract video URL from the response structure we saw
                video_url = get_result.content.video_url if hasattr(get_result, 'content') else None
                print(f"Video URL: {video_url}")
                yield "✅ Video generated successfully!", video_url
                return
                
            elif status == "failed":
                error_msg = get_result.error if hasattr(get_result, 'error') else "Unknown error"
                yield f"❌ Failed: {error_msg}", None
                return
            else:
                progress(0.3 + (attempts/max_attempts)*0.7, desc=f"Status: {status}")
                yield f"⏳ Status: {status}... (attempt {attempts + 1})", None
                time.sleep(1)
                attempts += 1
        
        yield "⏰ Timeout after 2 minutes", None
        
    except Exception as e:
        print(f"Error: {e}")
        yield f"❌ Error: {str(e)}", None

# Simple, clean interface
with gr.Blocks(title="BytePlus Video Generator", theme=gr.themes.Soft()) as demo:
    
    gr.Markdown("""
    # 🎥 BytePlus Video Generator
    
    Upload an image and describe the video you want to generate.
    """)
    
    # Show API key status
    if API_KEY:
        gr.Markdown("✅ **API Key:** Configured")
    else:
        gr.Markdown("❌ **API Key:** Not configured - please add 'Key' secret")
    
    with gr.Row():
        with gr.Column():
            # Image upload
            image_input = gr.Image(
                label="Upload Starting Image",
                type="pil",
                height=300
            )
            
            # Text prompt
            prompt = gr.Textbox(
                label="Video Description",
                lines=4,
                placeholder="Describe what you want to see...",
                value="At breakneck speed, drones thread through intricate obstacles or stunning natural wonders, delivering an immersive, heart-pounding flying experience. --duration 5 --camerafixed false"
            )
            
            # Generate button
            generate_btn = gr.Button("🚀 Generate Video", variant="primary", size="lg")
        
        with gr.Column():
            # Status
            status = gr.Textbox(
                label="Status",
                lines=4,
                interactive=False
            )
            
            # Video output
            video_output = gr.Video(
                label="Generated Video",
                interactive=False
            )
    
    # Example prompts
    gr.Markdown("---")
    gr.Markdown("### Example Prompts")
    with gr.Row():
        gr.Button("Nature").click(
            fn=lambda: "Aerial drone shot over mountains at sunrise, cinematic --duration 5 --camerafixed false",
            outputs=prompt
        )
        gr.Button("City").click(
            fn=lambda: "Fast drone racing through futuristic city streets --duration 5 --camerafixed false",
            outputs=prompt
        )
        gr.Button("Ocean").click(
            fn=lambda: "Drone following waves at golden hour --duration 5 --camerafixed false",
            outputs=prompt
        )
    
    # Connect the generate button
    generate_event = generate_btn.click(
        fn=generate_video,
        inputs=[image_input, prompt],
        outputs=[status, video_output]
    )

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0")