NSTiwari commited on
Commit
ded0e4a
·
verified ·
1 Parent(s): 58c61e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +203 -202
app.py CHANGED
@@ -1,203 +1,204 @@
1
- import os
2
- import io
3
- import time
4
- import base64
5
- import uuid
6
- import PIL.Image
7
- from flask import Flask, render_template, request, jsonify
8
- from dotenv import load_dotenv
9
-
10
- # Google Cloud & GenAI specific imports
11
- from google.cloud import storage
12
- from google.api_core import exceptions as google_exceptions
13
- from google import genai
14
- from google.genai import types
15
-
16
- # --- Configuration & Initialization ---
17
- load_dotenv('.env')
18
-
19
- app = Flask(__name__)
20
-
21
- LOCAL_IMAGE_DIR = os.path.join('static', 'generated_images')
22
- os.makedirs(LOCAL_IMAGE_DIR, exist_ok=True)
23
-
24
- # Gemini Image Generation Client (using your existing setup)
25
- API_KEY = os.environ.get("GOOGLE_API_KEY")
26
- MODEL_ID_IMAGE = 'gemini-2.0-flash-exp-image-generation'
27
-
28
- # Veo Video Generation Client (NEW)
29
- PROJECT_ID = os.environ.get("PROJECT_ID")
30
- LOCATION = os.environ.get("GOOGLE_CLOUD_REGION", "us-central1")
31
- GCS_BUCKET_NAME = os.environ.get("GCS_BUCKET_NAME")
32
- MODEL_ID_VIDEO = "veo-3.0-generate-preview" # Your Veo model ID
33
-
34
- if not all([API_KEY, PROJECT_ID, GCS_BUCKET_NAME, LOCATION]):
35
- raise RuntimeError("Missing required environment variables. Check your .env file.")
36
-
37
- # Initialize clients
38
- try:
39
- # Client for Gemini Image Generation
40
- gemini_image_client = genai.Client(api_key=API_KEY)
41
- print(f"Gemini Image Client initialized successfully for model: {MODEL_ID_IMAGE}")
42
-
43
- # Client for Veo Video Generation (Vertex AI)
44
- veo_video_client = genai.Client(vertexai=True, project=PROJECT_ID, location=LOCATION)
45
- print(f"Veo Video Client (Vertex AI) initialized successfully for project: {PROJECT_ID}")
46
-
47
- # Client for Google Cloud Storage
48
- gcs_client = storage.Client(project=PROJECT_ID)
49
- print("Google Cloud Storage Client initialized successfully.")
50
-
51
- except Exception as e:
52
- print(f"Error during client initialization: {e}")
53
- gemini_image_client = veo_video_client = gcs_client = None
54
-
55
-
56
- # --- Helper Function to Upload to GCS (NEW) ---
57
- def upload_bytes_to_gcs(image_bytes: bytes, bucket_name: str, destination_blob_name: str) -> str:
58
- """Uploads image bytes to GCS and returns the GCS URI."""
59
- if not gcs_client:
60
- raise ConnectionError("GCS client is not initialized.")
61
-
62
- bucket = gcs_client.bucket(bucket_name)
63
- blob = bucket.blob(destination_blob_name)
64
- blob.upload_from_string(image_bytes, content_type='image/png')
65
-
66
- gcs_uri = f"gs://{bucket_name}/{destination_blob_name}"
67
- print(f"Image successfully uploaded to {gcs_uri}")
68
- return gcs_uri
69
-
70
-
71
- # --- Main Routes ---
72
- @app.route('/')
73
- def index():
74
- """Renders the main HTML page."""
75
- return render_template('index.html')
76
-
77
- @app.route('/generate', methods=['POST'])
78
- def generate_video_from_sketch():
79
- """Full pipeline: sketch -> image -> video."""
80
- if not all([gemini_image_client, veo_video_client, gcs_client]):
81
- return jsonify({"error": "A server-side client is not initialized. Check server logs."}), 500
82
-
83
- if not request.json or 'image_data' not in request.json:
84
- return jsonify({"error": "Missing image_data in request"}), 400
85
-
86
- base64_image_data = request.json['image_data']
87
- user_prompt = request.json.get('prompt', '').strip()
88
-
89
- # --- Step 1: Generate Image with Gemini ---
90
- try:
91
- print("--- Step 1: Generating image from sketch with Gemini ---")
92
- if ',' in base64_image_data:
93
- base64_data = base64_image_data.split(',', 1)[1]
94
- else:
95
- base64_data = base64_image_data
96
-
97
- image_bytes = base64.b64decode(base64_data)
98
- sketch_pil_image = PIL.Image.open(io.BytesIO(image_bytes))
99
-
100
- # default_prompt = "Create a photorealistic image based on this sketch. Focus on realistic lighting, textures, and shadows to make it look like a photograph taken with a professional DSLR camera."
101
- default_prompt = "Convert this sketch into a photorealistic image as if it were taken from a real DSLR camera. The elements and objects should look real."
102
- #prompt_text = f"{default_prompt} {user_prompt}" if user_prompt else default_prompt
103
-
104
- response = gemini_image_client.models.generate_content(
105
- model=MODEL_ID_IMAGE,
106
- contents=[default_prompt, sketch_pil_image],
107
- config=types.GenerateContentConfig(response_modalities=['TEXT', 'IMAGE'])
108
- )
109
-
110
- if not response.candidates:
111
- raise ValueError("Gemini image generation returned no candidates.")
112
-
113
- generated_image_bytes = None
114
- for part in response.candidates[0].content.parts:
115
- if part.inline_data and part.inline_data.mime_type.startswith('image/'):
116
- generated_image_bytes = part.inline_data.data
117
- break
118
-
119
- if not generated_image_bytes:
120
- raise ValueError("Gemini did not return an image in the response.")
121
-
122
- print("Image generated successfully.")
123
-
124
- try:
125
- # Use a unique filename to prevent overwrites
126
- local_filename = f"generated-image-{uuid.uuid4()}.png"
127
- local_image_path = os.path.join(LOCAL_IMAGE_DIR, local_filename)
128
- # Write the bytes to a file in binary mode ('wb')
129
- with open(local_image_path, "wb") as f:
130
- f.write(generated_image_bytes)
131
- print(f"Image also saved locally to: {local_image_path}")
132
- except Exception as e:
133
- # This is not a critical error, so we just print a warning and continue.
134
- print(f"[Warning] Could not save image locally: {e}")
135
-
136
- except Exception as e:
137
- print(f"Error during Gemini image generation: {e}")
138
- return jsonify({"error": f"Failed to generate image: {e}"}), 500
139
-
140
- # --- Step 2 & 3: Upload Image to GCS and Generate Video with Veo ---
141
- try:
142
- print("\n--- Step 2: Uploading generated image to GCS ---")
143
- unique_id = uuid.uuid4()
144
- image_blob_name = f"images/generated-image-{unique_id}.png"
145
- output_gcs_prefix = f"gs://{GCS_BUCKET_NAME}/videos/" # Folder for video outputs
146
-
147
- image_gcs_uri = upload_bytes_to_gcs(generated_image_bytes, GCS_BUCKET_NAME, image_blob_name)
148
-
149
- print("\n--- Step 3: Calling Veo to generate video ---")
150
- default_video_prompt = "Animate this image. Add subtle, cinematic motion."
151
- video_prompt = f"{user_prompt}" if user_prompt else default_video_prompt
152
- print(video_prompt)
153
-
154
- operation = veo_video_client.models.generate_videos(
155
- model=MODEL_ID_VIDEO,
156
- prompt=video_prompt,
157
- image=types.Image(gcs_uri=image_gcs_uri, mime_type="image/png"),
158
- config=types.GenerateVideosConfig(
159
- aspect_ratio="16:9",
160
- output_gcs_uri=output_gcs_prefix,
161
- duration_seconds=8,
162
- person_generation="allow_adult",
163
- enhance_prompt=True,
164
- generate_audio=True, # Keep it simple for now
165
- ),
166
- )
167
-
168
-
169
- # WARNING: This is a synchronous poll, which will block the server thread.
170
- # For production, consider an asynchronous pattern (e.g., websockets or long polling).
171
- timeout_seconds = 300 # 5 minutes
172
- start_time = time.time()
173
- while not operation.done:
174
- if time.time() - start_time > timeout_seconds:
175
- raise TimeoutError("Video generation timed out.")
176
- time.sleep(15)
177
- # You must get the operation object again to refresh its status
178
- operation = veo_video_client.operations.get(operation)
179
- print(operation)
180
-
181
- print("Video generation operation complete.")
182
-
183
- if not operation.response or not operation.result.generated_videos:
184
- raise ValueError("Veo operation completed but returned no video.")
185
-
186
- video_gcs_uri = operation.result.generated_videos[0].video.uri
187
- print(f"Video saved to GCS at: {video_gcs_uri}")
188
-
189
- # Convert gs:// URI to public https:// URL
190
- video_blob_name = video_gcs_uri.replace(f"gs://{GCS_BUCKET_NAME}/", "")
191
-
192
- public_video_url = f"https://storage.googleapis.com/{GCS_BUCKET_NAME}/{video_blob_name}"
193
- print(f"Video generated successfully. Public URL: {public_video_url}")
194
-
195
- return jsonify({"generated_video_url": public_video_url})
196
-
197
- except Exception as e:
198
- print(f"An error occurred during video generation: {e}")
199
- return jsonify({"error": f"Failed to generate video: {e}"}), 500
200
-
201
-
202
- if __name__ == '__main__':
 
203
  app.run(debug=True, host='0.0.0.0', port=5000)
 
1
+ import os
2
+ import io
3
+ import time
4
+ import base64
5
+ import uuid
6
+ import PIL.Image
7
+ from flask import Flask, render_template, request, jsonify
8
+ from dotenv import load_dotenv
9
+
10
+ # Google Cloud & GenAI specific imports
11
+ from google.cloud import storage
12
+ from google.api_core import exceptions as google_exceptions
13
+ from google import genai
14
+ from google.genai import types
15
+
16
+ # --- Configuration & Initialization ---
17
+ # load_dotenv('.env')
18
+
19
+ app = Flask(__name__)
20
+
21
+ LOCAL_IMAGE_DIR = os.path.join('static', 'generated_images')
22
+ os.makedirs(LOCAL_IMAGE_DIR, exist_ok=True)
23
+
24
+ # Gemini Image Generation Client (using your existing setup)
25
+ API_KEY = os.environ.get("GOOGLE_API_KEY")
26
+ MODEL_ID_IMAGE = 'gemini-2.0-flash-exp-image-generation'
27
+
28
+ # Veo Video Generation Client (NEW)
29
+ PROJECT_ID = os.environ.get("PROJECT_ID")
30
+ LOCATION = os.environ.get("GOOGLE_CLOUD_REGION", "us-central1")
31
+ GCS_BUCKET_NAME = os.environ.get("GCS_BUCKET_NAME")
32
+ MODEL_ID_VIDEO = "veo-3.0-generate-preview" # Your Veo model ID
33
+
34
+ if not all([API_KEY, PROJECT_ID, GCS_BUCKET_NAME, LOCATION]):
35
+ raise RuntimeError("Missing required environment variables. Check your .env file.")
36
+
37
+ # Initialize clients
38
+ try:
39
+ # Client for Gemini Image Generation
40
+ gemini_image_client = genai.Client(api_key=API_KEY)
41
+ print(f"Gemini Image Client initialized successfully for model: {MODEL_ID_IMAGE}")
42
+
43
+ # Client for Veo Video Generation (Vertex AI)
44
+ veo_video_client = genai.Client(vertexai=True, project=PROJECT_ID, location=LOCATION)
45
+ print(f"Veo Video Client (Vertex AI) initialized successfully for project: {PROJECT_ID}")
46
+
47
+ # Client for Google Cloud Storage
48
+ gcs_client = storage.Client(project=PROJECT_ID)
49
+ print("Google Cloud Storage Client initialized successfully.")
50
+
51
+ except Exception as e:
52
+ print(f"Error during client initialization: {e}")
53
+ gemini_image_client = veo_video_client = gcs_client = None
54
+
55
+
56
+ # --- Helper Function to Upload to GCS (NEW) ---
57
+ def upload_bytes_to_gcs(image_bytes: bytes, bucket_name: str, destination_blob_name: str) -> str:
58
+ """Uploads image bytes to GCS and returns the GCS URI."""
59
+ if not gcs_client:
60
+ raise ConnectionError("GCS client is not initialized.")
61
+
62
+ bucket = gcs_client.bucket(bucket_name)
63
+ blob = bucket.blob(destination_blob_name)
64
+ blob.upload_from_string(image_bytes, content_type='image/png')
65
+
66
+ gcs_uri = f"gs://{bucket_name}/{destination_blob_name}"
67
+ print(f"Image successfully uploaded to {gcs_uri}")
68
+ return gcs_uri
69
+
70
+
71
+ # --- Main Routes ---
72
+ @app.route('/')
73
+ def index():
74
+ """Renders the main HTML page."""
75
+ return render_template('index.html')
76
+
77
+ @app.route('/generate', methods=['POST'])
78
+ def generate_video_from_sketch():
79
+ """Full pipeline: sketch -> image -> video."""
80
+ if not all([gemini_image_client]):
81
+ # if not all([gemini_image_client, veo_video_client, gcs_client]):
82
+ return jsonify({"error": "A server-side client is not initialized. Check server logs."}), 500
83
+
84
+ if not request.json or 'image_data' not in request.json:
85
+ return jsonify({"error": "Missing image_data in request"}), 400
86
+
87
+ base64_image_data = request.json['image_data']
88
+ user_prompt = request.json.get('prompt', '').strip()
89
+
90
+ # --- Step 1: Generate Image with Gemini ---
91
+ try:
92
+ print("--- Step 1: Generating image from sketch with Gemini ---")
93
+ if ',' in base64_image_data:
94
+ base64_data = base64_image_data.split(',', 1)[1]
95
+ else:
96
+ base64_data = base64_image_data
97
+
98
+ image_bytes = base64.b64decode(base64_data)
99
+ sketch_pil_image = PIL.Image.open(io.BytesIO(image_bytes))
100
+
101
+ # default_prompt = "Create a photorealistic image based on this sketch. Focus on realistic lighting, textures, and shadows to make it look like a photograph taken with a professional DSLR camera."
102
+ default_prompt = "Convert this sketch into a photorealistic image as if it were taken from a real DSLR camera. The elements and objects should look real."
103
+ #prompt_text = f"{default_prompt} {user_prompt}" if user_prompt else default_prompt
104
+
105
+ response = gemini_image_client.models.generate_content(
106
+ model=MODEL_ID_IMAGE,
107
+ contents=[default_prompt, sketch_pil_image],
108
+ config=types.GenerateContentConfig(response_modalities=['TEXT', 'IMAGE'])
109
+ )
110
+
111
+ if not response.candidates:
112
+ raise ValueError("Gemini image generation returned no candidates.")
113
+
114
+ generated_image_bytes = None
115
+ for part in response.candidates[0].content.parts:
116
+ if part.inline_data and part.inline_data.mime_type.startswith('image/'):
117
+ generated_image_bytes = part.inline_data.data
118
+ break
119
+
120
+ if not generated_image_bytes:
121
+ raise ValueError("Gemini did not return an image in the response.")
122
+
123
+ print("Image generated successfully.")
124
+
125
+ try:
126
+ # Use a unique filename to prevent overwrites
127
+ local_filename = f"generated-image-{uuid.uuid4()}.png"
128
+ local_image_path = os.path.join(LOCAL_IMAGE_DIR, local_filename)
129
+ # Write the bytes to a file in binary mode ('wb')
130
+ with open(local_image_path, "wb") as f:
131
+ f.write(generated_image_bytes)
132
+ print(f"Image also saved locally to: {local_image_path}")
133
+ except Exception as e:
134
+ # This is not a critical error, so we just print a warning and continue.
135
+ print(f"[Warning] Could not save image locally: {e}")
136
+
137
+ except Exception as e:
138
+ print(f"Error during Gemini image generation: {e}")
139
+ return jsonify({"error": f"Failed to generate image: {e}"}), 500
140
+
141
+ # --- Step 2 & 3: Upload Image to GCS and Generate Video with Veo ---
142
+ try:
143
+ print("\n--- Step 2: Uploading generated image to GCS ---")
144
+ unique_id = uuid.uuid4()
145
+ image_blob_name = f"images/generated-image-{unique_id}.png"
146
+ output_gcs_prefix = f"gs://{GCS_BUCKET_NAME}/videos/" # Folder for video outputs
147
+
148
+ image_gcs_uri = upload_bytes_to_gcs(generated_image_bytes, GCS_BUCKET_NAME, image_blob_name)
149
+
150
+ print("\n--- Step 3: Calling Veo to generate video ---")
151
+ default_video_prompt = "Animate this image. Add subtle, cinematic motion."
152
+ video_prompt = f"{user_prompt}" if user_prompt else default_video_prompt
153
+ print(video_prompt)
154
+
155
+ operation = veo_video_client.models.generate_videos(
156
+ model=MODEL_ID_VIDEO,
157
+ prompt=video_prompt,
158
+ image=types.Image(gcs_uri=image_gcs_uri, mime_type="image/png"),
159
+ config=types.GenerateVideosConfig(
160
+ aspect_ratio="16:9",
161
+ output_gcs_uri=output_gcs_prefix,
162
+ duration_seconds=8,
163
+ person_generation="allow_adult",
164
+ enhance_prompt=True,
165
+ generate_audio=True, # Keep it simple for now
166
+ ),
167
+ )
168
+
169
+
170
+ # WARNING: This is a synchronous poll, which will block the server thread.
171
+ # For production, consider an asynchronous pattern (e.g., websockets or long polling).
172
+ timeout_seconds = 300 # 5 minutes
173
+ start_time = time.time()
174
+ while not operation.done:
175
+ if time.time() - start_time > timeout_seconds:
176
+ raise TimeoutError("Video generation timed out.")
177
+ time.sleep(15)
178
+ # You must get the operation object again to refresh its status
179
+ operation = veo_video_client.operations.get(operation)
180
+ print(operation)
181
+
182
+ print("Video generation operation complete.")
183
+
184
+ if not operation.response or not operation.result.generated_videos:
185
+ raise ValueError("Veo operation completed but returned no video.")
186
+
187
+ video_gcs_uri = operation.result.generated_videos[0].video.uri
188
+ print(f"Video saved to GCS at: {video_gcs_uri}")
189
+
190
+ # Convert gs:// URI to public https:// URL
191
+ video_blob_name = video_gcs_uri.replace(f"gs://{GCS_BUCKET_NAME}/", "")
192
+
193
+ public_video_url = f"https://storage.googleapis.com/{GCS_BUCKET_NAME}/{video_blob_name}"
194
+ print(f"Video generated successfully. Public URL: {public_video_url}")
195
+
196
+ return jsonify({"generated_video_url": public_video_url})
197
+
198
+ except Exception as e:
199
+ print(f"An error occurred during video generation: {e}")
200
+ return jsonify({"error": f"Failed to generate video: {e}"}), 500
201
+
202
+
203
+ if __name__ == '__main__':
204
  app.run(debug=True, host='0.0.0.0', port=5000)