huchiahsi commited on
Commit
9c0afa9
·
1 Parent(s): 44dd167
Files changed (1) hide show
  1. image_examples.py +25 -38
image_examples.py CHANGED
@@ -279,102 +279,89 @@ def generate_video_from_image(image_path):
279
  with open(image_path, "rb") as img_file:
280
  img_bytes = img_file.read()
281
 
282
- # 簡單判斷 MIME type (可根據需要擴展)
283
  mime_type = "image/jpeg"
284
  if image_path.lower().endswith(".png"):
285
  mime_type = "image/png"
286
  elif image_path.lower().endswith(".webp"):
287
  mime_type = "image/webp"
288
 
289
- # 這是將本地圖片傳給 Veo 的關鍵步驟
290
- # 官方範例的 image = imagen.generated_images[0].image 是 Imagen API 的輸出
291
- # 我們需要自己建構這個 Image 物件
292
  image_for_veo = GeminiImage(image_bytes=img_bytes, mime_type=mime_type)
293
  app.logger.info(f"Successfully created GeminiImage object for Veo. MIME type: {mime_type}")
294
  except Exception as e:
295
  app.logger.error(f"Failed to read or convert image {image_path} for Veo: {e}")
296
- return image_path # 轉換失敗,回傳原圖路徑
297
 
298
  try:
299
  # 2. 呼叫 Veo API 生成影片
300
  operation = client.models.generate_videos(
301
- model="veo-2.0-generate-001", # 確認模型名稱正確
302
  prompt=prompt,
303
- image=image_for_veo, # 使用我們從本地圖片建立的 Image 物件
304
  config=types.GenerateVideosConfig(
305
  person_generation="dont_allow",
306
  aspect_ratio="16:9",
307
- number_of_videos=1 # 官方範例是 2,我們通常只需要1個影片給使用者
308
  ),
309
  )
310
  app.logger.info(f"Video generation initiated. Operation name: {operation.name}")
311
 
312
  # 3. 等待影片生成完成
313
  while not operation.done:
314
- app.logger.info(f"Waiting for video generation... Operation: {operation.name}, Status: checking...")
315
- time.sleep(20) # 與官方範例一致
316
- operation = client.operations.get(operation.name) # 更新操作狀態
317
 
318
  app.logger.info(f"Video generation operation completed. Done: {operation.done}")
319
 
320
  # 4. 處理並儲存生成的影片
321
  if operation.error:
322
- app.logger.error(f"Video generation failed with API error: {operation.error.message if hasattr(operation.error, 'message') else operation.error}")
323
  return image_path
324
 
325
  if operation.response and operation.response.generated_videos:
326
  image_dir = os.path.join(os.getcwd(), "images")
327
- os.makedirs(image_dir, exist_ok=True) # 確保目錄存在
328
 
329
  for n, generated_video_part in enumerate(operation.response.generated_videos):
330
- # generated_video_part.video 應該是 VideoData 物件
331
  if hasattr(generated_video_part, 'video') and generated_video_part.video:
332
  video_data = generated_video_part.video
333
 
334
  base, _ = os.path.splitext(os.path.basename(image_path))
335
- # 官方範例檔名 'with_image_input{n}.mp4',我們用原始檔名基礎
336
  fname = f"{base}_veo{n}.mp4"
337
  save_path = os.path.join(image_dir, fname)
338
 
339
  try:
340
- # 官方範例有 client.files.download(file=video.video)
341
- # 但 VideoData 物件通常有自己的 save 方法
342
- # 我們優先使用 video_data.save()
343
- if hasattr(video_data, 'save'):
344
- video_data.save(save_path)
345
- app.logger.info(f"Video successfully saved to: {save_path}")
346
- return save_path # 成功儲存第一個影片後即回傳
347
- # elif hasattr(video_data, 'uri'): # 如果沒有 save 但有 uri,可能需要下載
348
- # app.logger.info(f"VideoData has URI: {video_data.uri}. Download might be needed if save() is not available.")
349
- # # client.files.download(file=video_data) # 官方範例的下載方式
350
- # # downloaded_file = client.files.get(file=video_data.uri) # 另一種可能的下載
351
- # # with open(save_path, "wb") as f:
352
- # # f.write(downloaded_file.data) # 假設下載回傳的物件有 data 屬性
353
- # # app.logger.info(f"Video downloaded via URI and saved to: {save_path}")
354
- # # return save_path
355
- else:
356
- app.logger.error(f"VideoData for {fname} does not have a 'save' method or a downloadable 'uri'. Cannot save.")
357
-
358
  except Exception as e:
359
  app.logger.error(f"Error saving video {fname}: {e}")
360
  else:
361
  app.logger.warning(f"Generated video part {n} does not contain valid video data.")
362
 
363
  app.logger.warning("Processed all video parts but none were successfully saved.")
364
- return image_path # 如果迴圈結束都沒有成功儲存
365
 
366
  else:
367
  app.logger.warning("Video generation operation completed, but no videos found in the response.")
368
  return image_path
369
 
370
- except genai_errors.GoogleAPICallError as ge:
371
- app.logger.error(f"Google GenAI API Call Error during video generation: {ge}")
372
- return image_path
373
  except Exception as e:
374
  app.logger.error(f"An unexpected error occurred during video generation: {e}")
375
  return image_path
376
 
377
- app.logger.warning(f"Reached end of generate_video_from_image for {image_path} without returning a new video path.")
378
  return image_path
379
 
380
  # ... (如果你的應用程式是透過 gunicorn 執行,則不需要 if __name__ == "__main__":)
 
279
  with open(image_path, "rb") as img_file:
280
  img_bytes = img_file.read()
281
 
282
+ # 簡單判斷 MIME type
283
  mime_type = "image/jpeg"
284
  if image_path.lower().endswith(".png"):
285
  mime_type = "image/png"
286
  elif image_path.lower().endswith(".webp"):
287
  mime_type = "image/webp"
288
 
 
 
 
289
  image_for_veo = GeminiImage(image_bytes=img_bytes, mime_type=mime_type)
290
  app.logger.info(f"Successfully created GeminiImage object for Veo. MIME type: {mime_type}")
291
  except Exception as e:
292
  app.logger.error(f"Failed to read or convert image {image_path} for Veo: {e}")
293
+ return image_path
294
 
295
  try:
296
  # 2. 呼叫 Veo API 生成影片
297
  operation = client.models.generate_videos(
298
+ model="veo-2.0-generate-001",
299
  prompt=prompt,
300
+ image=image_for_veo,
301
  config=types.GenerateVideosConfig(
302
  person_generation="dont_allow",
303
  aspect_ratio="16:9",
304
+ number_of_videos=1
305
  ),
306
  )
307
  app.logger.info(f"Video generation initiated. Operation name: {operation.name}")
308
 
309
  # 3. 等待影片生成完成
310
  while not operation.done:
311
+ app.logger.info(f"Waiting for video generation... Operation: {operation.name}")
312
+ time.sleep(20)
313
+ operation = client.operations.get(operation)
314
 
315
  app.logger.info(f"Video generation operation completed. Done: {operation.done}")
316
 
317
  # 4. 處理並儲存生成的影片
318
  if operation.error:
319
+ app.logger.error(f"Video generation failed with API error: {operation.error}")
320
  return image_path
321
 
322
  if operation.response and operation.response.generated_videos:
323
  image_dir = os.path.join(os.getcwd(), "images")
324
+ os.makedirs(image_dir, exist_ok=True)
325
 
326
  for n, generated_video_part in enumerate(operation.response.generated_videos):
 
327
  if hasattr(generated_video_part, 'video') and generated_video_part.video:
328
  video_data = generated_video_part.video
329
 
330
  base, _ = os.path.splitext(os.path.basename(image_path))
 
331
  fname = f"{base}_veo{n}.mp4"
332
  save_path = os.path.join(image_dir, fname)
333
 
334
  try:
335
+ # 根據官方範例,先下載再儲存
336
+ client.files.download(file=video_data)
337
+ video_data.save(save_path)
338
+
339
+ # 生成完整的網址並 LOG 印出
340
+ video_filename = os.path.basename(save_path)
341
+ full_video_url = f"https://{base_url}/images/{video_filename}"
342
+
343
+ app.logger.info(f" Video successfully saved to: {save_path}")
344
+ app.logger.info(f"🔗 Full video URL for testing: {full_video_url}")
345
+ app.logger.info(f"📁 Video filename: {video_filename}")
346
+
347
+ return save_path
348
+
 
 
 
 
349
  except Exception as e:
350
  app.logger.error(f"Error saving video {fname}: {e}")
351
  else:
352
  app.logger.warning(f"Generated video part {n} does not contain valid video data.")
353
 
354
  app.logger.warning("Processed all video parts but none were successfully saved.")
355
+ return image_path
356
 
357
  else:
358
  app.logger.warning("Video generation operation completed, but no videos found in the response.")
359
  return image_path
360
 
 
 
 
361
  except Exception as e:
362
  app.logger.error(f"An unexpected error occurred during video generation: {e}")
363
  return image_path
364
 
 
365
  return image_path
366
 
367
  # ... (如果你的應用程式是透過 gunicorn 執行,則不需要 if __name__ == "__main__":)