File size: 6,767 Bytes
c1c563f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# app.py

import os
import gradio as gr
import requests
import json
import time
import base64
import google.auth
import google.auth.transport.requests
from huggingface_hub import login

# --- 1. Configuration and Authentication ---

# IMPORTANT: Replace with your Google Cloud Project ID
GCP_PROJECT_ID = "gen-lang-client-0193353123"
GCP_LOCATION = "us-central1"
MODEL_ID = "veo-3.0-generate-preview"
API_ENDPOINT = f"{GCP_LOCATION}-aiplatform.googleapis.com"
PREDICT_URL = f"https://{API_ENDPOINT}/v1/projects/{GCP_PROJECT_ID}/locations/{GCP_LOCATION}/publishers/google/models/{MODEL_ID}:predictLongRunning"
FETCH_URL = f"https://{API_ENDPOINT}/v1/projects/{GCP_PROJECT_ID}/locations/{GCP_LOCATION}/publishers/google/models/{MODEL_ID}:fetchPredictOperation"


# --- Authentication Block ---

# Part A: Hugging Face Hub Authentication (NEW)
# This section looks for a secret named 'HF_TOKEN' to log into the Hub.
hf_token = os.environ.get("HF_TOKEN")
if hf_token:
    print("Hugging Face token found. Logging in.")
    login(token=hf_token)
else:
    print("WARNING: Hugging Face token ('HF_TOKEN') not found. Hub-related features may be disabled.")


# Part B: Google Cloud Authentication (Unchanged)
# This section expects a secret named 'GOOGLE_APPLICATION_CREDENTIALS_JSON'
creds_json_str = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS_JSON")
if not creds_json_str:
    print("FATAL: 'GOOGLE_APPLICATION_CREDENTIALS_JSON' secret not found. App cannot authenticate with Google Cloud.")
    # Define a dummy function to show an error in the UI
    def generate_video(prompt):
        raise gr.Error("Authentication failed. Server is missing Google Cloud credentials. Please check the Hugging Face Space secrets.")
else:
    with open("gcp_creds.json", "w") as f:
        f.write(creds_json_str)
    
    SCOPES = ["https://www.googleapis.com/auth/cloud-platform"]
    credentials, _ = google.auth.load_credentials_from_file("gcp_creds.json", scopes=SCOPES)
    print("GCP credentials loaded successfully.")


    def get_access_token():
        """Generates a fresh short-lived access token for Google Cloud."""
        auth_req = google.auth.transport.requests.Request()
        credentials.refresh(auth_req)
        return credentials.token

    # --- 2. Core Video Generation Logic (Unchanged) ---

    def generate_video(prompt: str):
        """
        The main function to generate a video. It submits, polls, and returns the result.
        """
        if not prompt:
            raise gr.Error("Prompt cannot be empty.")
            
        yield "Status: Authenticating and submitting job...", None

        try:
            access_token = get_access_token()
            headers = {
                "Authorization": f"Bearer {access_token}",
                "Content-Type": "application/json",
            }

            # --- Step A: Submit the long-running prediction job ---
            payload = {
                "instances": [{"prompt": prompt}],
                "parameters": {
                    "aspectRatio": "16:9",
                    "sampleCount": 1,
                    "durationSeconds": 8,
                    "personGeneration": "allow_all",
                    "addWatermark": True,
                    "includeRaiReason": True,
                    "generateAudio": True,
                }
            }
            
            response = requests.post(PREDICT_URL, headers=headers, json=payload)
            response.raise_for_status() 

            operation_name = response.json()["name"]
            print(f"Successfully submitted job. Operation Name: {operation_name}")

            # --- Step B: Poll for the result ---
            MAX_POLL_ATTEMPTS = 60 
            for i in range(MAX_POLL_ATTEMPTS):
                status_message = f"Status: Job submitted. Polling for result (Attempt {i+1}/{MAX_POLL_ATTEMPTS})... Please wait."
                yield status_message, None

                access_token = get_access_token()
                headers["Authorization"] = f"Bearer {access_token}"
                
                fetch_payload = {"operationName": operation_name}
                poll_response = requests.post(FETCH_URL, headers=headers, json=fetch_payload)
                poll_response.raise_for_status()
                
                poll_result = poll_response.json()

                if poll_result.get("done"):
                    print("Job finished successfully.")
                    video_base64 = poll_result["response"]["predictions"][0]["bytesBase64Encoded"]
                    video_bytes = base64.b64decode(video_base64)
                    
                    temp_video_path = "generated_video.mp4"
                    with open(temp_video_path, "wb") as f:
                        f.write(video_bytes)
                    
                    yield "Status: Done!", temp_video_path
                    return
                
                time.sleep(10)

            raise gr.Error("Operation timed out after several minutes. The job may have failed or is taking too long.")

        except requests.exceptions.HTTPError as e:
            print(f"HTTP Error: {e.response.text}")
            raise gr.Error(f"API Error: {e.response.status_code}. Details: {e.response.text}")
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
            raise gr.Error(f"An unexpected error occurred: {str(e)}")


# --- 3. Gradio User Interface (Unchanged) ---

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🎬 Vertex AI VEO Video Generator")
    gr.Markdown(
        "Generate short videos from a text prompt using Google's VEO model. "
        "Generation can take several minutes. Please be patient."
    )
    
    with gr.Row():
        with gr.Column(scale=1):
            prompt_input = gr.Textbox(
                label="Prompt",
                placeholder="A majestic lion roaming the savanna at sunrise, cinematic 4K.",
                lines=3
            )
            submit_button = gr.Button("Generate Video", variant="primary")

        with gr.Column(scale=1):
            status_output = gr.Markdown("Status: Ready")
            video_output = gr.Video(label="Generated Video", interactive=False)
            
    gr.Examples(
        examples=[
            "A high-speed drone shot flying through a futuristic city with flying vehicles.",
            "A raccoon happily eating popcorn in a movie theater, cinematic lighting.",
            "A beautiful time-lapse of a flower blooming, from bud to full blossom, ultra-realistic.",
        ],
        inputs=prompt_input,
    )

    submit_button.click(
        fn=generate_video,
        inputs=prompt_input,
        outputs=[status_output, video_output]
    )

demo.launch()