Google Gemini Video Intelligence API 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. | | import json | | | import os | | | from typing import Any | | | from google import genai | | | 1. Configuration & Input | | | video id = "88I6IidylGc" | | | video id = "2lm1DFGFxPs" | | | YOUTUBE URL = f"https://www.youtube.com/watch?v={video id}" | | | processing mode = "agentic" | | | Use the script's directory as the base for all output files | | | SCRIPT DIR = os.path.dirname os.path.abspath file | | | OUTPUT FILE = os.path.join SCRIPT DIR, f"transcript concepts {video id} {processing mode}.json" | | | THOUGHTS OUTPUT FILE = os.path.join SCRIPT DIR, f"transcript concepts thoughts {video id} {processing mode}.json" | | | STEPS OUTPUT FILE = os.path.join SCRIPT DIR, f"transcript concepts steps {video id} {processing mode}.json" | | | 2. Schema Definition Standard JSON Schema with lowercase types | | | YOUTUBE RESPONSE SCHEMA: dict str, Any = { | | | "type": "object", | | | "properties": { | | | "videoTitle": {"type": "string"}, | | | "contentType": {"type": "string", "enum": "educational", "entertainment", "mixed" }, | | | "concepts": { | | | "type": "array", | | | "items": { | | | "type": "object", | | | "properties": { | | | "label": {"type": "string"}, | | | "timestamps": { | | | "type": "array", | | | "items": { | | | "type": "object", | | | "properties": { | | | "seconds": {"type": "string"}, | | | "description": {"type": "string"}, | | | "quote": {"type": "string"}, | | | "visual": {"type": "boolean"}, | | | "visualDescription": {"type": "string"}, | | | }, | | | "required": "seconds", "description", "quote" , | | | }, | | | }, | | | }, | | | "required": "label", "timestamps" , | | | }, | | | }, | | | "edges": { | | | "type": "array", | | | "items": { | | | "type": "object", | | | "properties": { | | | "fromLabel": {"type": "string"}, | | | "toLabel": {"type": "string"}, | | | "relation": {"type": "string", "enum": "detail", "sibling", "abstraction", "related" }, | | | "rationale": {"type": "string"}, | | | }, | | | "required": "fromLabel", "toLabel", "relation" , | | | }, | | | }, | | | "checkpoints": { | | | "type": "array", | | | "items": { | | | "type": "object", | | | "properties": { | | | "conceptLabel": {"type": "string"}, | | | "question": {"type": "string"}, | | | "answer": {"type": "string"}, | | | }, | | | "required": "conceptLabel", "question", "answer" , | | | }, | | | }, | | | }, | | | "required": "videoTitle", "contentType", "concepts" , | | | } | | | def serialize item item: Any - Any: | | | if hasattr item, "model dump" : | | | return item.model dump | | | elif hasattr item, " dict " : | | | return { | | | k: str v if not isinstance v, dict, list, int, float, bool, type None else v | | | for k, v in item. dict .items | | | } | | | return str item | | | def extract thoughts interaction: Any - list str : | | | thoughts = | | | May 2026 Steps Schema parsing | | | for step in getattr interaction, "steps", or : | | | step type = getattr step, "type", None or step.get "type" if isinstance step, dict else None | | | if step type == "thought": | | | summary = getattr step, "summary", or step.get "summary" if isinstance step, dict else | | | for item in summary: | | | text = getattr item, "text", None or item.get "text" if isinstance item, dict else None | | | if text: | | | thoughts.append text | | | Legacy fallback for older API versions | | | if not thoughts: | | | for candidate in getattr interaction, "candidates", or : | | | for part in getattr getattr candidate, "content", None , "parts", : | | | if getattr part, "thought", False : | | | thoughts.append part.text | | | return thoughts | | | def get interaction text interaction: Any - str: | | | if hasattr interaction, "outputs" and interaction.outputs: | | | for output in reversed interaction.outputs : | | | if hasattr output, "text" and output.text: | | | return output.text | | | if hasattr output, "content" and getattr output.content, "parts", None : | | | for part in output.content.parts: | | | if getattr part, "text", None and not getattr part, "thought", False : | | | return part.text | | | if hasattr interaction, "steps" and interaction.steps: | | | for step in reversed interaction.steps : | | | model res = getattr step, "model response", None or getattr step, "response", None | | | if model res and hasattr model res, "candidates" : | | | for candidate in model res.candidates or : | | | for part in getattr getattr candidate, "content", None , "parts", : | | | if getattr part, "text", None and not getattr part, "thought", False : | | | return part.text | | | return getattr interaction, "output text", "{}" | | | def analyze youtube video url: str, output path: str, thoughts path: str, steps path: str : | | | print "Sending request to Gemini via Interactions API..." | | | client = genai.Client | | | prompt = | | | f"Analyze the YouTube video {url} and identify its key structural units. " | | | "For each unit you must log the EXACT timecode in 'MM:SS' format as it first appears in the video's underlying audio stream. " | | | "Try to find at least 3 timestamps per concept label" | | | "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." | | | | | | interaction = client.interactions.create | | | model="gemini-3.5-flash-lite", | | | input= | | | { | | | "type": "text", | | | "text": prompt, | | | }, | | | { | | | "type": "video", | | | "uri": url, | | | "processing": processing mode | | | } | | | , | | | response format={ | | | "type": "text", | | | "mime type": "application/json", | | | "schema": YOUTUBE RESPONSE SCHEMA, | | | }, | | | generation config={ | | | "temperature": 0.1, | | | "thinking summaries": "auto", | | | }, | | | | | | 1. Save thinking process | | | thoughts = extract thoughts interaction | | | with open thoughts path, "w", encoding="utf-8" as f: | | | json.dump {"interaction id": getattr interaction, "id", None , "thoughts": thoughts}, f, indent=2 | | | 2. Save steps | | | raw steps = getattr interaction, "steps", or | | | serialized steps = serialize item step for step in raw steps | | | with open steps path, "w", encoding="utf-8" as f: | | | json.dump {"interaction id": getattr interaction, "id", None , "steps": serialized steps}, f, indent=2 | | | 3. Extract and parse structured text | | | raw text = get interaction text interaction | | | data = json.loads raw text | | | with open output path, "w", encoding="utf-8" as f: | | | json.dump data, f, indent=2, ensure ascii=False | | | print f"Successfully saved structured output to '{output path}' " | | | return data | | | result = analyze youtube video YOUTUBE URL, OUTPUT FILE, THOUGHTS OUTPUT FILE, STEPS OUTPUT FILE | | | print json.dumps result, indent=2 |