LLMOpsThe LLMOps lifecycle · MLflow 3 · FastAPI · Docker · AWS/Azure
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
50 small wins to finish your pathNext lesson →

Node one: indexing the video

The first node turns a video URL into text the model can read. It downloads the video, uploads it to Azure Video Indexer, waits for the indexing to finish, and parses the result into the transcript and on-screen text the state carries forward.

Last updated: 15 Oct, 2026 · LLMOps

The graph state declared the fields; this node fills the first of them. It is the multimodal step: the input is a video, and this is where it becomes something an LLM can work with.

Building the index-video node · from the End to End Multimodal LLMOps Project video · 1:59:00 to 2:02:00

What the node does, in order

  • Authenticate. It asks Azure for an ARM token, the management credential Video Indexer needs, then an account access token.
  • Download. yt-dlp fetches the video from the URL to a temporary file.
  • Upload and wait. The file is posted to Video Indexer, which returns a video ID; the node polls until indexing is done.
  • Parse. The result comes back as JSON; the node pulls out the transcript, the OCR text and the metadata, and writes them into the state.
python
import logging
from services.video_indexer import index_and_extract   # the service wrapper

log = logging.getLogger("brand-guardian")

def index_video_node(state: VideoAuditState) -> dict:
    log.info("index node: uploading %s to video indexer", state["video_url"])
    try:
        result = index_and_extract(state["video_url"])   # ARM token, download, upload, poll, parse
    except Exception as exc:
        # a failed download or an Azure timeout is recorded, not raised into the graph
        return {"system_errors": [f"index node: {exc}"], "status": "fail"}
    log.info("index node: indexed video %s", result["video_id"])
    return {
        "transcript": result["transcript"],
        "ocr_text": result["ocr_text"],
        "video_metadata": result["metadata"],
    }

The node returns only the keys it filled, and LangGraph merges them into the state. A failure returns a system_errors entry and a fail status instead of raising, so one bad download does not crash the whole audit, the pattern from Custom exceptions you can trace.

The index-video node as a left-to-right sequence: get an ARM token for Azure auth, download the video with yt-dlp, upload it to Video Indexer, poll until indexing finishes, and parse the returned JSON. The results, transcript, ocr_text and video_metadata, are written into the state. A red dashed box notes any failure is written to system_errors with a fail status rather than crashing the graph.
Watch out. Indexing a video is slow: the video notes a single end-to-end audit took 146 seconds, most of it here, downloading and indexing. This is why the node polls and why latency is a signal the project watches, not an afterthought.
Try it yourself
  • Trace what the state looks like after this node runs on a video whose download fails.
  • Explain why the node returns a dict of only the keys it changed, not the whole state.

Every expert started right here.