Spaces:
Running
Running
Constrain Gemini tutorial timestamps to real video duration
Browse filesPre-probe the video length and tell Gemini all timestamps must fall
within 0..duration, so it stops inventing segments past the end of the
video (which produced phantom articles with no clip). Also logs the
probed duration/codec before extraction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main.py
CHANGED
|
@@ -1242,6 +1242,29 @@ def tutorial_ingest():
|
|
| 1242 |
logger.info("[tutorial] STEP 1/5: received '%s' (%.2f MB, mime=%s)",
|
| 1243 |
video_title, len(video_bytes) / 1024 / 1024, mime_type)
|
| 1244 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1245 |
# Upload to Gemini Files API and wait for processing
|
| 1246 |
logger.info("[tutorial] STEP 2/5: uploading to Gemini Files API + waiting for ACTIVE...")
|
| 1247 |
gemini_file = _upload_video_to_gemini(video_bytes, mime_type, display_name=video_title)
|
|
@@ -1257,7 +1280,7 @@ def tutorial_ingest():
|
|
| 1257 |
t_extract = time.time()
|
| 1258 |
resp = _gemini_client.models.generate_content(
|
| 1259 |
model=GEMINI_MODEL,
|
| 1260 |
-
contents=[gemini_file,
|
| 1261 |
config=genai_types.GenerateContentConfig(
|
| 1262 |
response_mime_type="application/json",
|
| 1263 |
http_options=genai_types.HttpOptions(timeout=GEMINI_VIDEO_TIMEOUT_MS),
|
|
|
|
| 1242 |
logger.info("[tutorial] STEP 1/5: received '%s' (%.2f MB, mime=%s)",
|
| 1243 |
video_title, len(video_bytes) / 1024 / 1024, mime_type)
|
| 1244 |
|
| 1245 |
+
# Probe the real duration up front so we can constrain Gemini's timestamps to it —
|
| 1246 |
+
# otherwise the model sometimes invents segments past the end of the video.
|
| 1247 |
+
vid_dur_hint = 0.0
|
| 1248 |
+
try:
|
| 1249 |
+
with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as _pt:
|
| 1250 |
+
_pt.write(video_bytes)
|
| 1251 |
+
_probe_path = _pt.name
|
| 1252 |
+
vid_dur_hint = _video_duration(_probe_path)
|
| 1253 |
+
logger.info("[tutorial] probed duration %.1fs, codec %s",
|
| 1254 |
+
vid_dur_hint, _video_codec(_probe_path))
|
| 1255 |
+
os.remove(_probe_path)
|
| 1256 |
+
except Exception as e:
|
| 1257 |
+
logger.warning("[tutorial] pre-probe failed: %s", e)
|
| 1258 |
+
|
| 1259 |
+
extraction_prompt = TUTORIAL_VIDEO_PROMPT
|
| 1260 |
+
if vid_dur_hint and vid_dur_hint > 0:
|
| 1261 |
+
extraction_prompt += (
|
| 1262 |
+
f"\n\nIMPORTANT: This video is exactly {int(vid_dur_hint)} seconds long. "
|
| 1263 |
+
f"Every timestamp_start and timestamp_end MUST be between 0 and {int(vid_dur_hint)}. "
|
| 1264 |
+
f"Do not output any timestamp greater than {int(vid_dur_hint)}, and only describe "
|
| 1265 |
+
f"steps that actually occur within the video's length."
|
| 1266 |
+
)
|
| 1267 |
+
|
| 1268 |
# Upload to Gemini Files API and wait for processing
|
| 1269 |
logger.info("[tutorial] STEP 2/5: uploading to Gemini Files API + waiting for ACTIVE...")
|
| 1270 |
gemini_file = _upload_video_to_gemini(video_bytes, mime_type, display_name=video_title)
|
|
|
|
| 1280 |
t_extract = time.time()
|
| 1281 |
resp = _gemini_client.models.generate_content(
|
| 1282 |
model=GEMINI_MODEL,
|
| 1283 |
+
contents=[gemini_file, extraction_prompt],
|
| 1284 |
config=genai_types.GenerateContentConfig(
|
| 1285 |
response_mime_type="application/json",
|
| 1286 |
http_options=genai_types.HttpOptions(timeout=GEMINI_VIDEO_TIMEOUT_MS),
|