{"slug": "google-gemini-video-intelligence-api", "title": "Google Gemini Video Intelligence API", "summary": "A developer published a Python script that uses Google's Gemini API to analyze YouTube videos, extracting structured transcripts, concepts, timestamps, and relationship graphs into JSON. The tool supports an \"agentic\" processing mode and parses Gemini's reasoning steps, with a fallback for older API versions.", "body_md": "|  | import json | \n|  | import os | \n|  | from typing import Any | \n|  | from google import genai | \n|  | # 1. Configuration & Input | \n|  | # video_id = \"88I6IidylGc\" | \n|  | video_id = \"2lm1DFGFxPs\" | \n|  | YOUTUBE_URL = f\"https://www.youtube.com/watch?v={video_id}\" | \n|  | processing_mode = \"agentic\" | \n|  | # Use the script's directory as the base for all output files | \n|  | _SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | \n|  | OUTPUT_FILE = os.path.join(_SCRIPT_DIR, f\"transcript_concepts_{video_id}_{processing_mode}.json\") | \n|  | THOUGHTS_OUTPUT_FILE = os.path.join(_SCRIPT_DIR, f\"transcript_concepts_thoughts_{video_id}_{processing_mode}.json\") | \n|  | STEPS_OUTPUT_FILE = os.path.join(_SCRIPT_DIR, f\"transcript_concepts_steps_{video_id}_{processing_mode}.json\") | \n|  | # 2. Schema Definition (Standard JSON Schema with lowercase types) | \n|  | _YOUTUBE_RESPONSE_SCHEMA: dict[str, Any] = { | \n|  | \"type\": \"object\", | \n|  | \"properties\": { | \n|  | \"videoTitle\": {\"type\": \"string\"}, | \n|  | \"contentType\": {\"type\": \"string\", \"enum\": [\"educational\", \"entertainment\", \"mixed\"]}, | \n|  | \"concepts\": { | \n|  | \"type\": \"array\", | \n|  | \"items\": { | \n|  | \"type\": \"object\", | \n|  | \"properties\": { | \n|  | \"label\": {\"type\": \"string\"}, | \n|  | \"timestamps\": { | \n|  | \"type\": \"array\", | \n|  | \"items\": { | \n|  | \"type\": \"object\", | \n|  | \"properties\": { | \n|  | \"seconds\": {\"type\": \"string\"}, | \n|  | \"description\": {\"type\": \"string\"}, | \n|  | \"quote\": {\"type\": \"string\"}, | \n|  | \"visual\": {\"type\": \"boolean\"}, | \n|  | \"visualDescription\": {\"type\": \"string\"}, | \n|  | }, | \n|  | \"required\": [\"seconds\", \"description\", \"quote\"], | \n|  | }, | \n|  | }, | \n|  | }, | \n|  | \"required\": [\"label\", \"timestamps\"], | \n|  | }, | \n|  | }, | \n|  | \"edges\": { | \n|  | \"type\": \"array\", | \n|  | \"items\": { | \n|  | \"type\": \"object\", | \n|  | \"properties\": { | \n|  | \"fromLabel\": {\"type\": \"string\"}, | \n|  | \"toLabel\": {\"type\": \"string\"}, | \n|  | \"relation\": {\"type\": \"string\", \"enum\": [\"detail\", \"sibling\", \"abstraction\", \"related\"]}, | \n|  | \"rationale\": {\"type\": \"string\"}, | \n|  | }, | \n|  | \"required\": [\"fromLabel\", \"toLabel\", \"relation\"], | \n|  | }, | \n|  | }, | \n|  | \"checkpoints\": { | \n|  | \"type\": \"array\", | \n|  | \"items\": { | \n|  | \"type\": \"object\", | \n|  | \"properties\": { | \n|  | \"conceptLabel\": {\"type\": \"string\"}, | \n|  | \"question\": {\"type\": \"string\"}, | \n|  | \"answer\": {\"type\": \"string\"}, | \n|  | }, | \n|  | \"required\": [\"conceptLabel\", \"question\", \"answer\"], | \n|  | }, | \n|  | }, | \n|  | }, | \n|  | \"required\": [\"videoTitle\", \"contentType\", \"concepts\"], | \n|  | } | \n|  | def serialize_item(item: Any) -> Any: | \n|  | if hasattr(item, \"model_dump\"): | \n|  | return item.model_dump() | \n|  | elif hasattr(item, \"__dict__\"): | \n|  | return { | \n|  | k: str(v) if not isinstance(v, (dict, list, int, float, bool, type(None))) else v | \n|  | for k, v in item.__dict__.items() | \n|  | } | \n|  | return str(item) | \n|  | def extract_thoughts(interaction: Any) -> list[str]: | \n|  | thoughts = [] | \n|  | # May 2026 Steps Schema parsing | \n|  | for step in getattr(interaction, \"steps\", []) or []: | \n|  | step_type = getattr(step, \"type\", None) or (step.get(\"type\") if isinstance(step, dict) else None) | \n|  | if step_type == \"thought\": | \n|  | summary = getattr(step, \"summary\", []) or (step.get(\"summary\") if isinstance(step, dict) else []) | \n|  | for item in summary: | \n|  | text = getattr(item, \"text\", None) or (item.get(\"text\") if isinstance(item, dict) else None) | \n|  | if text: | \n|  | thoughts.append(text) | \n|  | # Legacy fallback for older API versions | \n|  | if not thoughts: | \n|  | for candidate in getattr(interaction, \"candidates\", []) or []: | \n|  | for part in getattr(getattr(candidate, \"content\", None), \"parts\", []): | \n|  | if getattr(part, \"thought\", False): | \n|  | thoughts.append(part.text) | \n|  | return thoughts | \n|  | def get_interaction_text(interaction: Any) -> str: | \n|  | if hasattr(interaction, \"outputs\") and interaction.outputs: | \n|  | for output in reversed(interaction.outputs): | \n|  | if hasattr(output, \"text\") and output.text: | \n|  | return output.text | \n|  | if hasattr(output, \"content\") and getattr(output.content, \"parts\", None): | \n|  | for part in output.content.parts: | \n|  | if getattr(part, \"text\", None) and not getattr(part, \"thought\", False): | \n|  | return part.text | \n|  | if hasattr(interaction, \"steps\") and interaction.steps: | \n|  | for step in reversed(interaction.steps): | \n|  | model_res = getattr(step, \"model_response\", None) or getattr(step, \"response\", None) | \n|  | if model_res and hasattr(model_res, \"candidates\"): | \n|  | for candidate in model_res.candidates or []: | \n|  | for part in getattr(getattr(candidate, \"content\", None), \"parts\", []): | \n|  | if getattr(part, \"text\", None) and not getattr(part, \"thought\", False): | \n|  | return part.text | \n|  | return getattr(interaction, \"output_text\", \"{}\") | \n|  | def analyze_youtube_video(url: str, output_path: str, thoughts_path: str, steps_path: str): | \n|  | print(\"Sending request to Gemini via Interactions API...\") | \n|  | client = genai.Client() | \n|  | prompt = ( | \n|  | f\"Analyze the YouTube video ({url}) and identify its key structural units. \" | \n|  | \"For each unit you must log the EXACT timecode in 'MM:SS' format as it first appears in the video's underlying audio stream. \" | \n|  | \"Try to find at least 3 timestamps per concept label\" | \n|  | \"Return in the seconds field in the response schema, every timestamp where a unit is first discussed in HH:MM:SS format or MM:SS if video is less than an hour.\" | \n|  | ) | \n|  | interaction = client.interactions.create( | \n|  | model=\"gemini-3.5-flash-lite\", | \n|  | input=[ | \n|  | { | \n|  | \"type\": \"text\", | \n|  | \"text\": prompt, | \n|  | }, | \n|  | { | \n|  | \"type\": \"video\", | \n|  | \"uri\": url, | \n|  | \"processing\": processing_mode | \n|  | } | \n|  | ], | \n|  | response_format={ | \n|  | \"type\": \"text\", | \n|  | \"mime_type\": \"application/json\", | \n|  | \"schema\": _YOUTUBE_RESPONSE_SCHEMA, | \n|  | }, | \n|  | generation_config={ | \n|  | \"temperature\": 0.1, | \n|  | \"thinking_summaries\": \"auto\", | \n|  | }, | \n|  | ) | \n|  | # 1. Save thinking process | \n|  | thoughts = extract_thoughts(interaction) | \n|  | with open(thoughts_path, \"w\", encoding=\"utf-8\") as f: | \n|  | json.dump({\"interaction_id\": getattr(interaction, \"id\", None), \"thoughts\": thoughts}, f, indent=2) | \n|  | # 2. Save steps | \n|  | raw_steps = getattr(interaction, \"steps\", []) or [] | \n|  | serialized_steps = [serialize_item(step) for step in raw_steps] | \n|  | with open(steps_path, \"w\", encoding=\"utf-8\") as f: | \n|  | json.dump({\"interaction_id\": getattr(interaction, \"id\", None), \"steps\": serialized_steps}, f, indent=2) | \n|  | # 3. Extract and parse structured text | \n|  | raw_text = get_interaction_text(interaction) | \n|  | data = json.loads(raw_text) | \n|  | with open(output_path, \"w\", encoding=\"utf-8\") as f: | \n|  | json.dump(data, f, indent=2, ensure_ascii=False) | \n|  | print(f\"Successfully saved structured output to '{output_path}'!\") | \n|  | return data | \n|  | result = analyze_youtube_video(YOUTUBE_URL, OUTPUT_FILE, THOUGHTS_OUTPUT_FILE, STEPS_OUTPUT_FILE) | \n|  | print(json.dumps(result, indent=2)) |", "url": "https://wpnews.pro/news/google-gemini-video-intelligence-api", "canonical_source": "https://gist.github.com/gitgithan/1515a2e093463b9fdb4163e6039778ee", "published_at": "2026-09-03 18:59:08+00:00", "updated_at": "2026-09-11 10:31:31.371790+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "ai-tools", "generative-ai", "developer-tools"], "entities": ["Google", "Gemini", "YouTube", "Python"], "alternates": {"html": "https://wpnews.pro/news/google-gemini-video-intelligence-api", "markdown": "https://wpnews.pro/news/google-gemini-video-intelligence-api.md", "text": "https://wpnews.pro/news/google-gemini-video-intelligence-api.txt", "jsonld": "https://wpnews.pro/news/google-gemini-video-intelligence-api.jsonld"}}