mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-06 01:17:46 +08:00
208 lines
5.8 KiB
Python
208 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
"""
|
||
|
|
Veo Video Generation Test Script
|
||
|
|
|
||
|
|
This script demonstrates video generation using Google's Veo API.
|
||
|
|
It sends a request to generate a video, polls for completion, and downloads the result.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
export GEMINI_API_KEY="your-api-key"
|
||
|
|
python test_veo.py
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import time
|
||
|
|
|
||
|
|
import requests
|
||
|
|
|
||
|
|
# Gemini API Base URL
|
||
|
|
BASE_URL = "http://localhost:8084/v1beta"
|
||
|
|
|
||
|
|
# Default polling interval in seconds
|
||
|
|
POLL_INTERVAL = 10
|
||
|
|
|
||
|
|
os.environ["GEMINI_API_KEY"] = "sk-PCr5oXZNKb9HcyzYqTIMvr8zXsIBK3WS"
|
||
|
|
|
||
|
|
|
||
|
|
def generate_video(api_key: str, prompt: str, model: str = "veo-3.1-fast-generate-preview") -> str:
|
||
|
|
"""
|
||
|
|
Send a request to generate a video and return the operation name.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
api_key: Gemini API key
|
||
|
|
prompt: Text prompt for video generation
|
||
|
|
model: Model name to use (default: veo-3.1-generate-preview)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Operation name for polling status
|
||
|
|
"""
|
||
|
|
url = f"{BASE_URL}/models/{model}:predictLongRunning"
|
||
|
|
headers = {
|
||
|
|
"x-goog-api-key": api_key,
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
}
|
||
|
|
payload = {
|
||
|
|
"instances": [
|
||
|
|
{
|
||
|
|
"prompt": prompt,
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
print(f"Sending video generation request to {model}...")
|
||
|
|
response = requests.post(url, headers=headers, json=payload, timeout=60)
|
||
|
|
response.raise_for_status()
|
||
|
|
|
||
|
|
data = response.json()
|
||
|
|
operation_name = data.get("name")
|
||
|
|
|
||
|
|
if not operation_name:
|
||
|
|
raise ValueError(f"No operation name in response: {data}")
|
||
|
|
|
||
|
|
print(f"Operation started: {operation_name}")
|
||
|
|
return operation_name
|
||
|
|
|
||
|
|
|
||
|
|
def poll_operation(api_key: str, operation_name: str, poll_interval: int = POLL_INTERVAL) -> dict:
|
||
|
|
"""
|
||
|
|
Poll the operation status until the video is ready.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
api_key: Gemini API key
|
||
|
|
operation_name: Operation name from generate_video
|
||
|
|
poll_interval: Seconds between polls (default: 10)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Final response dict containing the video URI
|
||
|
|
"""
|
||
|
|
url = f"{BASE_URL}/{operation_name}"
|
||
|
|
headers = {
|
||
|
|
"x-goog-api-key": api_key,
|
||
|
|
}
|
||
|
|
|
||
|
|
print(f"Polling operation status (every {poll_interval}s)...")
|
||
|
|
|
||
|
|
while True:
|
||
|
|
response = requests.get(url, headers=headers, timeout=60)
|
||
|
|
response.raise_for_status()
|
||
|
|
|
||
|
|
data = response.json()
|
||
|
|
is_done = data.get("done", False)
|
||
|
|
|
||
|
|
if is_done:
|
||
|
|
print("Operation completed!")
|
||
|
|
|
||
|
|
# Check for errors
|
||
|
|
if "error" in data:
|
||
|
|
error = data["error"]
|
||
|
|
raise RuntimeError(f"Operation failed: {error.get('message', error)}")
|
||
|
|
|
||
|
|
return data
|
||
|
|
|
||
|
|
# Show progress if available
|
||
|
|
metadata = data.get("metadata", {})
|
||
|
|
if metadata:
|
||
|
|
progress = metadata.get("progress", "unknown")
|
||
|
|
print(f" Progress: {progress}%")
|
||
|
|
|
||
|
|
time.sleep(poll_interval)
|
||
|
|
|
||
|
|
|
||
|
|
def download_video(api_key: str, video_uri: str, output_path: str = "dialogue_example.mp4") -> str:
|
||
|
|
"""
|
||
|
|
Download the generated video.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
api_key: Gemini API key
|
||
|
|
video_uri: URI of the generated video
|
||
|
|
output_path: Path to save the video (default: dialogue_example.mp4)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Path to the downloaded video
|
||
|
|
"""
|
||
|
|
headers = {
|
||
|
|
"x-goog-api-key": api_key,
|
||
|
|
}
|
||
|
|
|
||
|
|
print(f"Downloading video from: {video_uri}")
|
||
|
|
response = requests.get(video_uri, headers=headers, allow_redirects=True, timeout=300, stream=True)
|
||
|
|
response.raise_for_status()
|
||
|
|
|
||
|
|
with open(output_path, "wb") as f:
|
||
|
|
for chunk in response.iter_content(chunk_size=8192):
|
||
|
|
f.write(chunk)
|
||
|
|
|
||
|
|
file_size = os.path.getsize(output_path)
|
||
|
|
print(f"Video saved to: {output_path} ({file_size / 1024 / 1024:.2f} MB)")
|
||
|
|
return output_path
|
||
|
|
|
||
|
|
|
||
|
|
def extract_video_uri(response: dict) -> str:
|
||
|
|
"""
|
||
|
|
Extract the video URI from the operation response.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
response: Final operation response dict
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Video download URI
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
video_response = response["response"]["generateVideoResponse"]
|
||
|
|
samples = video_response["generatedSamples"]
|
||
|
|
video_uri = samples[0]["video"]["uri"]
|
||
|
|
return video_uri
|
||
|
|
except (KeyError, IndexError) as e:
|
||
|
|
raise ValueError(f"Could not extract video URI from response: {response}") from e
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> None:
|
||
|
|
"""Main entry point."""
|
||
|
|
# Get API key from environment
|
||
|
|
api_key = os.environ.get("GEMINI_API_KEY")
|
||
|
|
if not api_key:
|
||
|
|
print("Error: GEMINI_API_KEY environment variable not set", file=sys.stderr)
|
||
|
|
print("Usage: export GEMINI_API_KEY='your-api-key' && python test_veo.py", file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
# Default prompt (same as the bash script)
|
||
|
|
prompt = (
|
||
|
|
"A close up of two people staring at a cryptic drawing on a wall, "
|
||
|
|
"torchlight flickering. A man murmurs, \"This must be it. That's the secret code.\" "
|
||
|
|
"The woman looks at him and whispering excitedly, \"What did you find?\""
|
||
|
|
)
|
||
|
|
|
||
|
|
# Allow custom prompt via command line argument
|
||
|
|
if len(sys.argv) > 1:
|
||
|
|
prompt = " ".join(sys.argv[1:])
|
||
|
|
print(f"Using custom prompt: {prompt}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Step 1: Start video generation
|
||
|
|
operation_name = generate_video(api_key, prompt)
|
||
|
|
|
||
|
|
# Step 2: Poll until complete
|
||
|
|
final_response = poll_operation(api_key, operation_name)
|
||
|
|
|
||
|
|
# Step 3: Extract video URI and download
|
||
|
|
video_uri = extract_video_uri(final_response)
|
||
|
|
download_video(api_key, video_uri)
|
||
|
|
|
||
|
|
print("\nVideo generation complete!")
|
||
|
|
|
||
|
|
except requests.exceptions.HTTPError as e:
|
||
|
|
print(f"HTTP Error: {e}", file=sys.stderr)
|
||
|
|
if e.response is not None:
|
||
|
|
print(f"Response: {e.response.text}", file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Error: {e}", file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|