MySafeCode's picture
Create app.py
fb0aa05 verified
import gradio as gr
import requests
import os
import time
import json
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Get API key from environment (your "Key" secret)
API_KEY = os.environ.get("Key", "")
if not API_KEY:
logger.error("❌ No API key found in environment variable 'Key'")
else:
logger.info(f"✅ API key loaded (length: {len(API_KEY)})")
def generate_video(prompt_text, image_url, model_id, progress=gr.Progress()):
"""Generate video using direct API calls (curl approach)"""
if not API_KEY:
yield "❌ API key not configured. Please set the 'Key' secret.", None
return
try:
progress(0, desc="Starting request...")
# Use the path from curl command
url = "https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}" # Same as curl: -H "Authorization: Bearer $ARK_API_KEY"
}
# Prepare the request body exactly like curl
data = {
"model": model_id,
"content": [
{
"type": "text",
"text": prompt_text
},
{
"type": "image_url",
"image_url": {
"url": image_url
}
}
]
}
logger.info(f"📤 Sending request to: {url}")
logger.info(f"📝 Model: {model_id}")
# Make the POST request (like curl -X POST)
response = requests.post(url, headers=headers, json=data)
logger.info(f"📥 Response status: {response.status_code}")
if response.status_code != 200:
error_msg = f"Error {response.status_code}: {response.text}"
logger.error(f"❌ {error_msg}")
yield error_msg, None
return
result = response.json()
task_id = result.get('id')
if not task_id:
yield "❌ No task ID in response", None
return
logger.info(f"✅ Task created: {task_id}")
yield f"Task created: {task_id}", None
# Poll for results (like the while loop in original code)
progress(0.2, desc="Waiting for generation...")
# Status URL (adjust if different)
status_url = f"https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks/{task_id}"
max_attempts = 60
attempts = 0
while attempts < max_attempts:
progress(0.2 + (attempts / max_attempts) * 0.7,
desc=f"Polling... ({attempts + 1}/{max_attempts})")
status_response = requests.get(status_url, headers=headers)
if status_response.status_code != 200:
yield f"❌ Error checking status: {status_response.text}", None
return
status_result = status_response.json()
status = status_result.get('status')
logger.info(f"Status: {status}")
if status == "succeeded":
progress(1.0, desc="Complete!")
# Extract video URL (adjust based on actual response structure)
video_url = None
if 'output' in status_result and status_result['output']:
if isinstance(status_result['output'], list) and len(status_result['output']) > 0:
video_url = status_result['output'][0].get('video_url')
elif isinstance(status_result['output'], dict):
video_url = status_result['output'].get('video_url')
if video_url:
yield "✅ Video generated successfully!", video_url
else:
yield "✅ Task completed (no video URL in response)", None
return
elif status == "failed":
error = status_result.get('error', 'Unknown error')
yield f"❌ Task failed: {error}", None
return
else:
yield f"⏳ Status: {status}...", None
time.sleep(1)
attempts += 1
yield "⏰ Timeout: Task took too long", None
except Exception as e:
logger.error(f"❌ Exception: {str(e)}")
yield f"❌ Error: {str(e)}", None
# Simple Gradio interface
with gr.Blocks(title="BytePlus Video Generator", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🎥 BytePlus Video Generator
Using direct API calls (curl approach)
""")
# 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():
model_id = gr.Textbox(
label="Model ID",
value="seedance-1-5-pro-251215"
)
image_url = gr.Textbox(
label="Image URL",
value="https://ark-doc.tos-ap-southeast-1.bytepluses.com/seepro_i2v%20.png"
)
prompt = gr.Textbox(
label="Prompt",
lines=4,
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_btn = gr.Button("🚀 Generate", variant="primary")
with gr.Column():
status = gr.Textbox(label="Status", lines=3)
video = gr.Video(label="Generated Video")
video_url = gr.Textbox(label="Video URL")
# Example prompts
gr.Markdown("---")
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
)
# Generate
generate_btn.click(
fn=generate_video,
inputs=[prompt, image_url, model_id],
outputs=[status, video]
).then(
fn=lambda v: v if v else "",
inputs=[video],
outputs=[video_url]
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0")