> ## Documentation Index
> Fetch the complete documentation index at: https://docs.videodb.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Keyword Search

> Create custom video compilations by searching for keywords and phrases

<a href="https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/examples/Keyword_Search_1.ipynb" target="_blank">
  <img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" noZoom />
</a>

From an hour long video, want to create a fun compilation of every moment Mark Zuckerberg says 'metaverse'?

<iframe className="w-full aspect-video rounded-xl" src="https://console.videodb.io/player?url=https://stream.videodb.io/v3/published/manifests/3bc3a55b-29aa-46d5-9368-cd965af66518.m3u8" title="Keyword Search Result - Metaverse Compilation" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

## Overview

In this tutorial, let's explore the powerful functionality of Keyword Search in VideoDB. This feature enables users to efficiently locate any keyword or phrase within their video assets, streamlining the process of content discovery.

<img src="https://mintcdn.com/videodb/6KL5X6-sIPSRpEUt/assets/examples/fun-with-keyword-search.webp?fit=max&auto=format&n=6KL5X6-sIPSRpEUt&q=85&s=4f85bc908b7929bfb78b8f043a6aa5a1" style={{width: "auto", height: "auto"}} alt="Fun keyword search example with Mark Zuckerberg metaverse compilation" width="1554" height="1022" data-path="assets/examples/fun-with-keyword-search.webp" />

## Setup

### Installing packages

<CodeGroup>
  ```python Python theme={null}
  !pip install videodb
  ```

  ```javascript Node.js theme={null}
  npm install videodb
  ```
</CodeGroup>

### API Keys

Before proceeding, ensure access to [VideoDB](https://videodb.io)

Get your API key from [VideoDB Console](https://console.videodb.io/auth?utm_source=docs_videodb_io\&utm_medium=docs_link\&utm_campaign=console_auth\&utm_content=docs_link\&id=docs). ( Free for first 50 uploads, No credit card required)

## Steps

### Step 1: Connect to VideoDB

Begin by establishing a connection to VideoDB using your API key

<CodeGroup>
  ```python Python theme={null}
  import videodb

  # Set your API key
  api_key = "your_api_key"

  # Connect to VideoDB
  conn = videodb.connect(api_key=api_key)
  coll = conn.get_collection()
  ```

  ```javascript Node.js theme={null}
  import { connect } from 'videodb';

  const conn = await connect({ apiKey: process.env.VIDEO_DB_API_KEY });
  const coll = await conn.getCollection();
  ```
</CodeGroup>

### Step 2: Upload Video

Upload the video to your VideoDB collection. You can upload the video asset from your local device or from a YouTube URL to upload the video from its source. This works as the base video for all the Keyword Search queries.

<CodeGroup>
  ```python Python theme={null}
  video = coll.upload(url="https://www.youtube.com/watch?v=Uvufun6xer8")
  video.play()
  ```

  ```javascript Node.js theme={null}
  const video = await coll.uploadURL({
    url: "https://www.youtube.com/watch?v=Uvufun6xer8"
  });
  console.log(video.playerUrl);
  ```
</CodeGroup>

You can upload from your local file system too by passing `file_path` in `upload()`.

For this tutorial, we'll run a Keyword Search on the following video:

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/Uvufun6xer8" title="Keyword Search Tutorial Video" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

### Step 3: Index Spoken Words

Index the spoken words in your video to enable accurate keyword search.

<CodeGroup>
  ```python Python theme={null}
  import re
  understanding = video.understand(
      analyzers=[{"type": "spoken_words", "name": "transcript"}],
  )
  understanding.wait_until_complete()
  transcript_analyzer = understanding.get_analyzer("transcript")
  word_records = []
  seen_words = set()
  for scene in transcript_analyzer.get_output()["scenes"]:
      for word in scene.get("data", {}).get("words", []):
          text = re.sub(r"^\W+|\W+$", "", word.get("text", "")).casefold()
          start, end = float(word["start"]), float(word["end"])
          key = (text, round(start, 3), round(end, 3))
          if text and key not in seen_words:
              seen_words.add(key)
              word_records.append({"start": start, "end": end, "text": text})
  word_records.sort(key=lambda record: (record["start"], record["end"]))
  word_index = video.index(
      name="transcript_keyword_words",
      source=word_records,
      use_for=["query"],
      fields={"fts": ["text"], "filter": ["text"]},
  )
  word_index.wait_until_complete()
  ```

  ```javascript Node.js theme={null}
  const understanding = await video.understand([
    { type: "spoken_words", name: "transcript" },
  ]);
  await understanding.waitUntilComplete();

  const transcriptAnalyzer = await understanding.getAnalyzer("transcript");
  const transcriptOutput = await transcriptAnalyzer.getOutput();
  const wordRecords = transcriptOutput.scenes
    .flatMap((scene) => scene.data.words || [])
    .map((word) => ({
      start: word.start,
      end: word.end,
      text: word.text.replace(/^\W+|\W+$/g, "").toLowerCase(),
    }))
    .filter((word) => word.text);
  wordRecords.sort((left, right) => left.start - right.start || left.end - right.end);

  const wordIndex = await video.index(wordRecords, {
    name: "transcript_keyword_words",
    useFor: ["query"],
    fields: { fts: ["text"], filter: ["text"] },
  });
  await wordIndex.waitUntilComplete();
  ```
</CodeGroup>

### Step 4: Search for any keyword

Use `video.query(index_id=..., filter=...)` to retrieve the timed transcript moments that contain a word or phrase. The word-record index keeps both `fts` and `filter` support on its `text` field.

<CodeGroup>
  ```python Python theme={null}
  keyword = "metaverse".casefold()
  results = video.query(
      index_id=word_index.index_id,
      filter=[{"field": "text", "op": "==", "value": keyword}],
      limit=50,
      sort=[("start", "asc")],
  )
  for shot in results.get_shots()[:10]:
      print(f"{shot.start:.2f}s – {shot.end:.2f}s: {shot.text}")
  ```

  ```javascript Node.js theme={null}
  const keyword = "metaverse";
  const results = await video.query({
    indexId: wordIndex.indexId,
    filter: [{ field: "text", op: "==", value: keyword }],
    limit: 50,
    sort: [["start", "asc"]],
  });

  for (const shot of results.getShots().slice(0, 10)) {
    console.log(`${shot.start.toFixed(2)}s – ${shot.end.toFixed(2)}s: ${shot.text}`);
  }
  ```
</CodeGroup>

### Step 5: Preview and Share

Preview your video with a compilation of all the clips matching your search query. You can access the stream link alongside the preview to share the Keyword Search result with others.

<CodeGroup>
  ```python Python theme={null}
  stream_url = results.compile()
  print("Stream URL:", stream_url)
  results.play()
  ```

  ```javascript Node.js theme={null}
  await results.compile();
  console.log(results.playerUrl);
  ```
</CodeGroup>

<iframe className="w-full aspect-video rounded-xl" src="https://console.videodb.io/player?url=https://stream.videodb.io/v3/published/manifests/3bc3a55b-29aa-46d5-9368-cd965af66518.m3u8" title="Keyword Search Result - Metaverse Compilation" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

### Bonus: Refining Keyword Search results by adding padding

Some keyword search results/compilations may appear slightly choppy, or the cuts may feel abrupt. We can solve this issue by using VideoDB’s padding controls. Here’s how it works:

The resulting shots can be made smoother by including a little more context from before and after the matching timestamps. That's exactly what padding controls enable:

Using the Editor SDK's `Track` and `Clip` pattern, we can create a timeline with padding:

1. Create a timeline and track using `Timeline()` and `Track()`
2. Create a `VideoAsset` with `id` and `start` parameters (where `start` is adjusted by subtracting padding)
3. Wrap each asset in a `Clip` with the appropriate duration (adding padding on both ends)
4. Add clips to the track sequentially using `track.add_clip(start_time, clip)`

<CodeGroup>
  ```python Python theme={null}
  from videodb import play_stream
  from videodb.editor import Timeline, Track, Clip, VideoAsset

  timeline = Timeline(conn)

  # Add padding for smoother cuts
  padding = 0.4

  # Create main track
  track = Track()
  seeker = 0

  # Compile Video from search results
  for shot in results.get_shots():
      start_with_padding = max(0, shot.start - padding)
      duration = (shot.end + padding) - start_with_padding

      asset = VideoAsset(id=shot.video_id, start=start_with_padding)
      clip = Clip(asset=asset, duration=duration)
      track.add_clip(seeker, clip)

      seeker += duration

  timeline.add_track(track)

  stream_url = timeline.generate_stream()
  play_stream(stream_url)
  ```

  ```javascript Node.js theme={null}
  import { EditorTimeline, Track, Clip, EditorVideoAsset } from "videodb";

  const timeline = new EditorTimeline(conn);
  const padding = 0.4;
  const track = new Track();
  let seeker = 0;

  for (const shot of results.getShots()) {
    const startWithPadding = Math.max(0, shot.start - padding);
    const duration = shot.end + padding - startWithPadding;
    const asset = new EditorVideoAsset({
      id: shot.videoId,
      start: startWithPadding,
    });
    const clip = new Clip({ asset, duration });
    track.addClip(seeker, clip);
    seeker += duration;
  }

  timeline.addTrack(track);

  const streamUrl = await timeline.generateStream();
  console.log(streamUrl);
  ```
</CodeGroup>

### Here's the result for the same video, but improved using padding control.

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/MQVTtET7LM8" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

### Conclusion

Keyword Search in VideoDB empowers users to extract valuable insights from their video assets with ease.

## More Examples

Check out these fun experiments with Keyword search 👇

1. So basically it's "basically"

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/4gjl-WCpbXg" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

2. The untold story of "generative" AI

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/FW-OgmJLEWM" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

## Next Steps

<Card icon="notebook" title="Explore Full Notebook" href="https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/examples/Keyword_Search_1.ipynb">
  Open the complete implementation in Google Colab with all code examples.
</Card>

## Related Tutorials

<CardGroup cols={2}>
  <Card title="Multimodal Search" icon="brain" href="/examples-and-tutorials/video-rag/multimodal-search">
    Combine spoken word and visual search for comprehensive queries
  </Card>

  <Card title="Character Extraction" icon="user" href="/examples-and-tutorials/video-rag/character-clips">
    Extract clips featuring specific characters or people instantly
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.