Spaces:
Sleeping
Sleeping
Gaurav vashistha commited on
Commit ·
683cf64
1
Parent(s): 3598ffe
Fix Veo generation loop with op.result()
Browse files
agent.py
CHANGED
|
@@ -8,9 +8,8 @@ from google import genai
|
|
| 8 |
from google.genai import types
|
| 9 |
from config import Settings
|
| 10 |
from utils import download_to_temp, download_blob, save_video_bytes, update_job_status, stitch_videos
|
| 11 |
-
|
| 12 |
logging.basicConfig(level=logging.INFO)
|
| 13 |
-
logger = logging.getLogger(
|
| 14 |
|
| 15 |
def get_file_hash(filepath):
|
| 16 |
"""Calculates MD5 hash of file to prevent duplicate uploads."""
|
|
@@ -52,7 +51,7 @@ def analyze_only(path_a, path_c, job_id=None):
|
|
| 52 |
{
|
| 53 |
"analysis_a": "Brief description of Video A",
|
| 54 |
"analysis_c": "Brief description of Video C",
|
| 55 |
-
"visual_prompt_b": "A surreal, seamless morphing prompt that transforms A into C. DO NOT use words like 'dissolve' or 'cut'."
|
| 56 |
}
|
| 57 |
"""
|
| 58 |
update_job_status(job_id, "analyzing", 30, "Director drafting creative morph...")
|
|
@@ -87,27 +86,32 @@ def analyze_only(path_a, path_c, job_id=None):
|
|
| 87 |
def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, motion):
|
| 88 |
update_job_status(job_id, "generating", 50, "Production started (Veo 3.1)...")
|
| 89 |
full_prompt = f"{style} style. {prompt} Soundtrack: {audio}"
|
| 90 |
-
if neg:
|
| 91 |
-
full_prompt += f" --no {neg}"
|
| 92 |
|
| 93 |
job_failed = False
|
| 94 |
try:
|
| 95 |
if Settings.GCP_PROJECT_ID:
|
| 96 |
client = genai.Client(vertexai=True, project=Settings.GCP_PROJECT_ID, location=Settings.GCP_LOCATION)
|
|
|
|
|
|
|
| 97 |
op = client.models.generate_videos(
|
| 98 |
model='veo-3.1-generate-preview',
|
| 99 |
prompt=full_prompt,
|
| 100 |
config=types.GenerateVideosConfig(number_of_videos=1)
|
| 101 |
)
|
| 102 |
|
| 103 |
-
#
|
| 104 |
-
while not op.done
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
if
|
| 107 |
-
vid =
|
| 108 |
bridge_path = None
|
| 109 |
|
| 110 |
-
# Handle Video Download
|
| 111 |
if vid.video.uri:
|
| 112 |
bridge_path = tempfile.mktemp(suffix=".mp4")
|
| 113 |
download_blob(vid.video.uri, bridge_path)
|
|
@@ -115,33 +119,32 @@ def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, m
|
|
| 115 |
bridge_path = save_video_bytes(vid.video.video_bytes)
|
| 116 |
|
| 117 |
if bridge_path:
|
| 118 |
-
#
|
| 119 |
-
update_job_status(job_id, "stitching", 80, "
|
| 120 |
-
|
|
|
|
|
|
|
| 121 |
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
# If stitch fails, log it and return ONLY the bridge.
|
| 129 |
-
# DO NOT FAIL THE JOB.
|
| 130 |
-
logging.error(f"Stitch failed, returning bridge only: {e}")
|
| 131 |
-
update_job_status(job_id, "completed", 100, "Stitch failed (Bridge Saved).", video_url=bridge_path)
|
| 132 |
return
|
| 133 |
else:
|
| 134 |
raise Exception("Veo returned no videos.")
|
| 135 |
else:
|
| 136 |
raise Exception("GCP_PROJECT_ID not set.")
|
| 137 |
except Exception as e:
|
| 138 |
-
logging.error(f"Gen
|
| 139 |
update_job_status(job_id, "error", 0, f"Error: {e}")
|
| 140 |
job_failed = True
|
| 141 |
finally:
|
|
|
|
| 142 |
if not job_failed:
|
| 143 |
try:
|
| 144 |
with open(f"outputs/{job_id}.json", "r") as f:
|
| 145 |
if json.load(f).get("status") not in ["completed", "error"]:
|
| 146 |
-
update_job_status(job_id, "error", 0, "
|
| 147 |
except: pass
|
|
|
|
| 8 |
from google.genai import types
|
| 9 |
from config import Settings
|
| 10 |
from utils import download_to_temp, download_blob, save_video_bytes, update_job_status, stitch_videos
|
|
|
|
| 11 |
logging.basicConfig(level=logging.INFO)
|
| 12 |
+
logger = logging.getLogger(name)
|
| 13 |
|
| 14 |
def get_file_hash(filepath):
|
| 15 |
"""Calculates MD5 hash of file to prevent duplicate uploads."""
|
|
|
|
| 51 |
{
|
| 52 |
"analysis_a": "Brief description of Video A",
|
| 53 |
"analysis_c": "Brief description of Video C",
|
| 54 |
+
"visual_prompt_b": "A surreal, seamless morphing prompt that transforms A into C. DO NOT use words like 'dissolve' or 'cut'. Focus on shape and texture transformation."
|
| 55 |
}
|
| 56 |
"""
|
| 57 |
update_job_status(job_id, "analyzing", 30, "Director drafting creative morph...")
|
|
|
|
| 86 |
def generate_only(prompt, path_a, path_c, job_id, style, audio, neg, guidance, motion):
|
| 87 |
update_job_status(job_id, "generating", 50, "Production started (Veo 3.1)...")
|
| 88 |
full_prompt = f"{style} style. {prompt} Soundtrack: {audio}"
|
| 89 |
+
if neg: full_prompt += f" --no {neg}"
|
|
|
|
| 90 |
|
| 91 |
job_failed = False
|
| 92 |
try:
|
| 93 |
if Settings.GCP_PROJECT_ID:
|
| 94 |
client = genai.Client(vertexai=True, project=Settings.GCP_PROJECT_ID, location=Settings.GCP_LOCATION)
|
| 95 |
+
|
| 96 |
+
# 1. Start Long-Running Operation
|
| 97 |
op = client.models.generate_videos(
|
| 98 |
model='veo-3.1-generate-preview',
|
| 99 |
prompt=full_prompt,
|
| 100 |
config=types.GenerateVideosConfig(number_of_videos=1)
|
| 101 |
)
|
| 102 |
|
| 103 |
+
# 2. CRITICAL FIX: Use .result() to wait.
|
| 104 |
+
# 'while not op.done' causes infinite loops if op doesn't auto-refresh.
|
| 105 |
+
if hasattr(op, 'result'):
|
| 106 |
+
result = op.result()
|
| 107 |
+
else:
|
| 108 |
+
# Fallback for synchronous responses
|
| 109 |
+
result = op
|
| 110 |
|
| 111 |
+
if result and result.generated_videos:
|
| 112 |
+
vid = result.generated_videos[0]
|
| 113 |
bridge_path = None
|
| 114 |
|
|
|
|
| 115 |
if vid.video.uri:
|
| 116 |
bridge_path = tempfile.mktemp(suffix=".mp4")
|
| 117 |
download_blob(vid.video.uri, bridge_path)
|
|
|
|
| 119 |
bridge_path = save_video_bytes(vid.video.video_bytes)
|
| 120 |
|
| 121 |
if bridge_path:
|
| 122 |
+
# 3. Stitching Phase (Optional/Bypassable)
|
| 123 |
+
update_job_status(job_id, "stitching", 80, "Checking Stitch Capability...", video_url=bridge_path)
|
| 124 |
+
|
| 125 |
+
final_cut = os.path.join("outputs", f"{job_id}_merged_temp.mp4")
|
| 126 |
+
merged_path = stitch_videos(path_a, bridge_path, path_c, final_cut)
|
| 127 |
|
| 128 |
+
if merged_path:
|
| 129 |
+
msg = "Done! (Merged)"
|
| 130 |
+
else:
|
| 131 |
+
msg = "Done! (Bridge Only - No Stitch)"
|
| 132 |
+
|
| 133 |
+
update_job_status(job_id, "completed", 100, msg, video_url=bridge_path, merged_video_url=merged_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
return
|
| 135 |
else:
|
| 136 |
raise Exception("Veo returned no videos.")
|
| 137 |
else:
|
| 138 |
raise Exception("GCP_PROJECT_ID not set.")
|
| 139 |
except Exception as e:
|
| 140 |
+
logging.error(f"Gen Job Failed: {e}")
|
| 141 |
update_job_status(job_id, "error", 0, f"Error: {e}")
|
| 142 |
job_failed = True
|
| 143 |
finally:
|
| 144 |
+
# 4. DEAD MAN'S SWITCH
|
| 145 |
if not job_failed:
|
| 146 |
try:
|
| 147 |
with open(f"outputs/{job_id}.json", "r") as f:
|
| 148 |
if json.load(f).get("status") not in ["completed", "error"]:
|
| 149 |
+
update_job_status(job_id, "error", 0, "Job timed out (Zombie).")
|
| 150 |
except: pass
|