Intermediate · 25 min

Build a Video Search Engine in Python

A video search engine finds a moment inside a clip, not only a filename. You sample frames or keyframes, embed them with a model such as CLIP, index those vectors, rank by similarity, and serve the query over HTTP.

The Challenge

A typical video search stack splits ownership: object storage for the files, a database for titles, a job to dump frames, an embedding service, a vector database, and a FastAPI app that tries to keep those copies aligned. Changing the sample rate or the model means touching every joint.

The Solution

Pixeltable is the unified multimodal backend for that job. One Python file declares the video table, the frame view, the CLIP index, and the HTTP route (TableModel + FastAPIRouter + the pxt CLI). Insert a clip; frames and embeddings follow that row. The same file deploys to Pixeltable Cloud (Limited Beta). It is not an LLM wrapper and not a standalone vector database.

Implementation Guide

Step-by-step walkthrough with code examples

Step 1 of 5

Declare the video table

Store each clip as a typed Video column plus the metadata you will filter on. Apply the file with pxt schema update; do not call create_table at import.

python
1import pixeltable as pxt
2
3TableModel = pxt.model_base()
4
5class Videos(TableModel, name='videos'):
6 title: pxt.String
7 video: pxt.Video
The catalog records that the column is video. Later columns can extract frames or audio because the type is known, not guessed from a path string.

Glue stack vs one Python file

JobTypical glue stackOne Python file
Store the clipObject storage plus a metadata databaseA Video column on the table you own
Sample framesA scheduled ffmpeg job between storesA frame_iterator view on that clip
Embed stillsAn embedding service you poll and copy outCLIP as the index function on the frame column
Rank similar momentsA separate vector database you keep alignedEmbeddingIndex on the same frame view
Serve searchA FastAPI app that joins those copiesFastAPIRouter declared beside the tables
DeployRewrite the joints against hosted servicesThe same file on Pixeltable Cloud (Limited Beta)

Key Benefits

One Video column instead of an object store plus a metadata database for the same clip
Frame extraction and embeddings are columns, not an Airflow job you rerun by hand
The CLIP index lives on the frame view, so a separate vector database is optional
FastAPIRouter exposes insert and search from the same file the pxt CLI applies
The same file deploys to Pixeltable Cloud (Limited Beta)

Real Applications

Find a spoken or visual moment in a training library
Search marketing footage with a sentence or a still
Review a clip at a timestamp instead of scrubbing the file
Add detections or a transcript as extra columns on the same rows

Prerequisites

Basic Python
A few video files or URLs to insert
Python 3.11+
pip install -U 'pixeltable[serve]'
ffmpeg for video decoding
torch and transformers for CLIP (openai/clip-vit-base-patch32)
Optional: openai-whisper and pixeltable-yolox for transcript and detection columns

Frequently asked questions

What is a keyframe, and where is the frame timestamp?
A keyframe (I-frame) is a complete picture in the compressed stream. frame_iterator(..., keyframes_only=True) emits only those stills. fps=1 samples at a fixed rate instead. You cannot set fps, num_frames, and keyframes_only together. Each row keeps frame_attrs["index"] (stream index) and frame_attrs["time"] (seconds from the start of the clip), so a hit is a moment, not only a filename.
Do I still need a separate vector database?
You need an embedding index so similar frames can be ranked. That index can live on the frame view. A separate vector database is optional when something else is already the source of truth. Pixeltable declares pxt.EmbeddingIndex on the table that holds the frames.
Is this an LLM wrapper or a hosted video API?
Neither. The tables, the CLIP index, and the HTTP route are schema you own in one Python file. A chat model that watches one clip on every question is not an index. A hosted video API that returns JSON and no table leaves you without the rows.
Can I search with an image as well as a sentence?
Yes, if the embedding model places text and images in one space. CLIP-style models do. Frames.frame.similarity(string=query) ranks stills against a sentence; Frames.frame.similarity(image=still) ranks against a picture.

Ready to get started?

Install Pixeltable and start building in minutes. One Python file for tables, indexes, and HTTP. Pixeltable Cloud is Limited Beta.