> ## 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.

# Dynamic Streams

> Create personalized video streams with data overlays

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

## Introduction

Imagine you're watching a captivating keynote session from your favorite conference, and you’re welcomed with a personalized stream just for you.

This tutorial demonstrates how to create dynamic video streams by integrating data from custom databases and external APIs. We'll use a practical example: a recording of a [Config 2023](https://www.youtube.com/watch?v=Nmv8XdFiej0) keynote session. By using VideoDB, we'll show how companies like [Figma](https://www.figma.com/files/recents-and-sharing?fuid=940498258276625180) can personalize the viewing experience for their audience, delivering a richer and more engaging experience.

We'll showcase how to:

* **Fetch data from a random user API** to represent a hypothetical viewer.
* Integrate this data into a **custom VideoDB timeline**.
* Create a **personalized stream** that dynamically displays relevant information alongside the keynote video.

This tutorial is your guide to unlocking the potential of dynamic video streams and transforming your video content with personalized experiences.

## 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/). ( 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 Base Video

Upload and play the video to ensure it's correctly loaded. We'll be using [this video](https://www.youtube.com/watch?v=Nmv8XdFiej0) for the purpose of this tutorial.

<CodeGroup>
  ```python Python theme={null}
  # Upload and play a video from a URL
  video = coll.upload(url="https://www.youtube.com/watch?v=Nmv8XdFiej0")
  video.play()

  # Alternatively, get a video from your VideoDB collection
  # video = coll.get_video('VIDEO_ID_HERE')
  # video.play()
  ```

  ```javascript Node.js theme={null}
  // Upload and play a video from a URL
  const video = await coll.uploadURL({ url: "https://www.youtube.com/watch?v=Nmv8XdFiej0" });
  console.log(video.playerUrl);

  // Alternatively, get a video from your VideoDB collection
  // const video = await coll.getVideo('VIDEO_ID_HERE');
  // console.log(video.playerUrl);
  ```
</CodeGroup>

### Step 3: Fetch Data from a Random User API

This code fetches a random user's data (name and picture) from the "randomuser.me" API.  You can adapt this to retrieve data from any relevant API (e.g., product data, news articles) for your use case.

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

  # Make a request to the Randomizer API
  response = requests.get('https://randomuser.me/api/?results=1&nat=us,ca,gb,au')
  data = response.json()

  # Extract relevant information
  first_name = data['results'][0]['name']['first']
  medium_picture = data['results'][0]['picture']['medium']
  ```

  ```javascript Node.js theme={null}
  // Make a request to the Randomizer API
  const response = await fetch('https://randomuser.me/api/?results=1&nat=us,ca,gb,au');
  const data = await response.json();

  // Extract relevant information
  const firstName = data.results[0].name.first;
  const mediumPicture = data.results[0].picture.medium;
  ```
</CodeGroup>

### Step 4: Upload the image to VideoDB

* First we download the image to local storage
* Then we use the local path to upload it to VideoDB

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

  # 1. Download the image locally
  local_path = "my_local_image.jpg"

  response = requests.get(medium_picture)
  if response.status_code == 200:
      with open(local_path, 'wb') as f:
          f.write(response.content)
      print(f"Image downloaded successfully to: {local_path}")
  else:
      print(f"Failed to download image. Status code: {response.status_code}")

  # 2. Upload using the local file path

  from videodb import play_stream, MediaType
  image = coll.upload(file_path=local_path, media_type=MediaType.image)

  print(f"Image uploaded to VideoDB: {image.id}")
  ```

  ```javascript Node.js theme={null}
  import * as fs from 'fs';
  import { MediaType } from 'videodb';

  // 1. Download the image locally
  const localPath = "my_local_image.jpg";

  const imgResponse = await fetch(mediumPicture);
  if (imgResponse.ok) {
      const buffer = Buffer.from(await imgResponse.arrayBuffer());
      fs.writeFileSync(localPath, buffer);
      console.log(`Image downloaded successfully to: ${localPath}`);
  } else {
      console.log(`Failed to download image. Status code: ${imgResponse.status}`);
  }

  // 2. Upload using the local file path
  const image = await coll.uploadFile({
      filePath: localPath,
      mediaType: MediaType.image
  });

  console.log(`Image uploaded to VideoDB: ${image.id}`);
  ```
</CodeGroup>

### Step 5: Create VideoDB Assets

We create VideoDB assets for the base video, the user's name (text), and their picture (image) using the new Editor SDK. The `Font` and `Background` objects allow us to customize the appearance of text elements.

<CodeGroup>
  ```python Python theme={null}
  from videodb.editor import (
      Timeline, Track, Clip,
      VideoAsset, TextAsset, ImageAsset,
      Font, Background, Alignment, HorizontalAlignment, VerticalAlignment,
      Position, Offset, Fit)

  # 1. Video Asset (Base background)
  video_asset = VideoAsset(id=video.id, start=0)

  # 2. Name Asset (Top)
  name_asset = TextAsset(
      text=f'Hi {first_name} !',
      font=Font(family="Montserrat", size=60, color="#000000"),
      background=Background(color="#D2C11D", border_width=20, opacity=1.0),
      alignment=Alignment(horizontal=HorizontalAlignment.center, vertical=VerticalAlignment.top),)

  # 3. Message Asset (Middle)
  cmon_asset = TextAsset(
      text="Here are your favorite moments",
      font=Font(family="Montserrat", size=60, color="#D2C11D"),
      background=Background(color="#000000", border_width=20, opacity=1.0),
      alignment=Alignment(horizontal=HorizontalAlignment.center, vertical=VerticalAlignment.center),)

  # 4. Image Asset (Bottom)
  image_asset = ImageAsset(id=image.id)
  ```

  ```javascript Node.js theme={null}
  import {
      EditorTimeline, Track, Clip,
      EditorVideoAsset, EditorTextAsset, EditorImageAsset,
      Font, Background, Alignment, HorizontalAlignment, VerticalAlignment,
      Position, Offset
  } from 'videodb';

  // 1. Video Asset (Base background)
  const videoAsset = new EditorVideoAsset({ id: video.id, start: 0 });

  // 2. Name Asset (Top)
  const nameAsset = new EditorTextAsset({
      text: `Hi ${firstName} !`,
      font: new Font({ family: "Montserrat", size: 60, color: "#000000" }),
      background: new Background({ color: "#D2C11D", borderWidth: 20, opacity: 1.0 }),
      alignment: new Alignment({ horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.top })
  });

  // 3. Message Asset (Middle)
  const cmonAsset = new EditorTextAsset({
      text: "Here are your favorite moments",
      font: new Font({ family: "Montserrat", size: 60, color: "#D2C11D" }),
      background: new Background({ color: "#000000", borderWidth: 20, opacity: 1.0 }),
      alignment: new Alignment({ horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.center })
  });

  // 4. Image Asset (Bottom)
  const imageAsset = new EditorImageAsset({ id: image.id });
  ```
</CodeGroup>

### ↔️ Step 6: Create the VideoDB Timeline

Using the `Track` and `Clip` pattern, we arrange and layer assets to create a dynamic video stream. The main video goes on one track, while overlays (name, message, image) go on separate tracks with their start times.

<CodeGroup>
  ```python Python theme={null}
  # Create the timeline
  timeline = Timeline(conn)

  # --- Track 1: Main Video ---
  video_track = Track()
  video_clip = Clip(asset=video_asset, duration=float(video.length))
  video_track.add_clip(0, video_clip)
  timeline.add_track(video_track)

  # --- Track 2: Overlays ---
  overlay_track = Track()

  # 1. Add Name Overlay (Top)
  name_clip = Clip(
      asset=name_asset,
      duration=4,
      position=Position.top,
      offset=Offset(y=0.15))
  overlay_track.add_clip(5, name_clip)

  # 2. Add Message Overlay (Center)
  cmon_clip = Clip(
      asset=cmon_asset,
      duration=4,
      position=Position.center,)
  overlay_track.add_clip(5, cmon_clip)

  # 3. Add Image Overlay (Bottom)
  image_clip = Clip(
      asset=image_asset,
      duration=4,
      position=Position.bottom,
      scale=2,
      fit=Fit.none,
      offset=Offset(y=-0.15))
  overlay_track.add_clip(5, image_clip)

  timeline.add_track(overlay_track)
  ```

  ```javascript Node.js theme={null}
  // Create the timeline
  const timeline = new EditorTimeline(conn);

  // --- Track 1: Main Video ---
  const videoTrack = new Track();
  const videoClip = new Clip({ asset: videoAsset, duration: parseFloat(video.length) });
  videoTrack.addClip(0, videoClip);
  timeline.addTrack(videoTrack);

  // --- Track 2: Overlays ---
  const overlayTrack = new Track();

  // 1. Add Name Overlay (Top)
  const nameClip = new Clip({
      asset: nameAsset,
      duration: 4,
      position: Position.top,
      offset: new Offset({ y: 0.15 })
  });
  overlayTrack.addClip(5, nameClip);

  // 2. Add Message Overlay (Center)
  const cmonClip = new Clip({
      asset: cmonAsset,
      duration: 4,
      position: Position.center
  });
  overlayTrack.addClip(5, cmonClip);

  // 3. Add Image Overlay (Bottom)
  const imageClip = new Clip({
      asset: imageAsset,
      duration: 4,
      position: Position.bottom,
      scale: 2,
      offset: new Offset({ y: -0.15 })
  });
  overlayTrack.addClip(5, imageClip);

  timeline.addTrack(overlayTrack);
  ```
</CodeGroup>

### ▶️ Step 7: Generate and Play the Personalized Stream

The `generate_stream()` method creates a streamable URL for your personalized video stream. You can then use `play_stream()`  to preview it in your browser.

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

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

  ```javascript Node.js theme={null}
  const streamUrl = await timeline.generateStream();
  console.log(streamUrl);
  ```
</CodeGroup>

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

## Conclusion

This tutorial showcased how to create personalized video streams using VideoDB. By integrating data from external APIs and custom databases, you can enhance your video content, personalize user experiences, and unlock new possibilities for engagement. Explore various data sources, experiment with different integrations, and customize your video streams to suit your specific needs.

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

## Related Tutorials

<CardGroup cols={2}>
  <Card title="Intro & Outro" icon="play" href="/examples-and-tutorials/programmatic-editing/intro-outro">
    Auto-add opening and closing sequences
  </Card>

  <Card title="Dynamic Ads" icon="target" href="/examples-and-tutorials/programmatic-editing/dynamic-ads">
    Insert personalized ads per viewer
  </Card>
</CardGroup>
