Skip to main content
Open In Colab

The Idea

You’ve uploaded hundreds of videos this year. You’ve run thousands of searches. Your automation has processed countless scenes. But how do you celebrate and share those numbers in a way that actually captivates people? Traditional dashboards and spreadsheets are boring. What if you could transform your video analytics into a shareable cinematic recap video?

What You’ll Build

Turn your video analytics into a shareable recap video. This system creates a cinematic journey through your video stats:
  • Total minutes uploaded with growth metrics
  • Search activity visualization
  • Clips generated via automation
  • Scenes and frames analyzed
  • All set to music with dynamic video grids and text overlays
All powered by VideoDB’s Editor SDK — turning data into a visual story through code.

Setup

Install Dependencies

pip install videodb

Connect to VideoDB

import json
import random
import videodb
from videodb import MediaType, play_stream

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

Implementation

Step 1: Calculate Your Video Metrics

# Get all videos from your collection
videos = coll.get_videos()

TOTAL_SECONDS = 0
for v in videos:
  TOTAL_SECONDS += v.length

# Core metrics
TOTAL_MINUTES = round(TOTAL_SECONDS / 60, 1)
TOTAL_SEARCHES = 2573
CLIPS_GENERATED = 574
SCENES_ANALYZED = 248
FRAMES_ANALYZED = 3200

# Previous year comparison
PREV_YEAR_MINUTES = 6400

# Time conversions
TOTAL_HOURS = round(TOTAL_MINUTES / 60, 1)
TOTAL_DAYS = round(TOTAL_HOURS / 24, 1)

# Growth percentage
MINUTES_GROWTH = round(((TOTAL_MINUTES - PREV_YEAR_MINUTES) / PREV_YEAR_MINUTES) * 100)

Step 2: Upload Background Music

# Upload background music as audio
audio = coll.upload("https://youtu.be/vIEi8AoP_Fw?si=AUVUt6Komqtt7rWI", media_type="audio")

Step 3: Create Timeline with 5 Sections

Initialize the timeline and set up base configuration:
from videodb.editor import (
    Timeline, Track, Clip, VideoAsset, AudioAsset, TextAsset,
    Offset, Transition, Font, Filter,
)

# Use subset of videos
videos = videos[20:40]

# Create timeline
timeline = Timeline(conn)
timeline.background = "#E85E00"
track = Track()
video_track = Track()

# Add background music
b_music = Clip(
    asset=AudioAsset(id=audio.id, start=0, volume=1),
    duration=28,
)
track.add_clip(4, b_music)

Step 4: Build Intro Section (0-5s)

Create a dynamic mosaic effect with 60 video clips:
# Intro text
intro_text = Clip(
    asset=TextAsset(
        text="2025 was a blur. Let's index it.",
        font=Font(size=60, family="Roboto Bold"),
    ),
    duration=5,
    transition=Transition(in_="fade"),
)
track.add_clip(1, intro_text)

# Create 60 video clips with varying scales and random positioning
for i in range(60):
    scale_value = 0.5 + (i * 0.025)  # Scale: 0.5 to 0.975
    start_time = random.randint(0, 4)
    volume = 0.5
    if start_time == 3:
        volume = 0.3
    elif start_time == 4:
        volume = 0.3

    video = random.choice(videos)

    video_clip = Clip(
        asset=VideoAsset(
            id=video.id,
            start=random.randint(10, int(video.length - 10)),
            volume=volume,
        ),
        duration=1,
        scale=scale_value,
        fit=None,
        offset=Offset(x=random.uniform(-0.5, 0.5), y=random.uniform(-0.5, 0.5)),
    )

    video_track.add_clip(start=start_time, clip=video_clip)

Step 5: Build Upload Metrics Section (6-13s)

Display total minutes uploaded with zoom-out effect:
# Section 1 text
section1_text = Clip(
    asset=TextAsset(
        text=f"You uploaded {TOTAL_MINUTES:,} minutes\nof video content.\n\nThat's {TOTAL_HOURS} hours.\nOr {TOTAL_DAYS} full days of footage.\n\n{MINUTES_GROWTH}% from 2024",
        font=Font(size=48, family="default"),
    ),
    duration=7,
)
track.add_clip(6, section1_text)

# Zoom out effect with greyscale filter
for i in range(20):
    scale_value = 0.5 + (i * 0.0263)  # Scale: 0.5 to 1.0
    start_time = 6 + (i * 0.175)

    video = random.choice(videos)
    video_start = random.randint(10, max(11, int(video.length - 10)))

    video_clip = Clip(
        asset=VideoAsset(
            id=video.id,
            start=video_start,
            volume=0.15,
        ),
        duration=1,
        scale=scale_value,
        fit="crop",
        filter=Filter.greyscale,
    )

    video_track.add_clip(start=start_time, clip=video_clip)

Step 6: Build Search Analytics Section (13-19s)

Create 3 rows of horizontal scanning video clips:
# Section 2 text
section2_text = Clip(
    asset=TextAsset(
        text=f"{TOTAL_SEARCHES:,} questions asked.\n\nYour videos aren't just stored—\nthey're understood.",
        font=Font(size=58, family="default"),
    ),
    duration=6,
)
track.add_clip(13, section2_text)

# Create horizontal scanning effect with 3 rows
rows = [
    {"y": -0.5, "num_clips": 20, "duration": 0.25, "scale": 0.35},  # Top row
    {"y": 0, "num_clips": 15, "duration": 0.33, "scale": 0.45},     # Middle row
    {"y": 0.5, "num_clips": 12, "duration": 0.42, "scale": 0.35},   # Bottom row
]

clip_idx = 0
for row in rows:
    for i in range(row["num_clips"]):
        x_pos = -0.9 + (i * 0.1)
        video = videos[clip_idx % len(videos)]

        video_start = 30 + (clip_idx % 10)
        if video_start > video.length - 10:
            video_start = 15

        video_clip = Clip(
            asset=VideoAsset(
                id=video.id,
                start=video_start,
                volume=0.15,
            ),
            duration=row["duration"],
            scale=row["scale"],
            fit="crop",
            offset=Offset(x=x_pos, y=row["y"]),
        )
        video_track.add_clip(start=12 + (i * row["duration"]), clip=video_clip)
        clip_idx += 1

Step 7: Build Automation Stats Section (19-25s)

Large background clips with cycling effect:
# Section 3 text
section3_text = Clip(
    asset=TextAsset(
        text=f"{CLIPS_GENERATED:,} clips\ngenerated via code.\n\nAutomation at its finest.",
        font=Font(size=62, family="default"),
    ),
    duration=6,
)
track.add_clip(19, section3_text)

# Cycling background effect
for i in range(3):
    video = videos[(i + 5) % len(videos)]
    video_start = 20 + (i * 15)
    if video_start > video.length - 10:
        video_start = 10

    bg_clip = Clip(
        asset=VideoAsset(
            id=video.id,
            start=video_start,
            volume=0.05,
        ),
        duration=2,
        scale=1.2,
        fit="crop",
        filter=Filter.greyscale,
        opacity=0.2,
        z_index=0,
    )
    video_track.add_clip(start=19 + (i * 2), clip=bg_clip)

Step 8: Build Intelligence Metrics Section (25-31s)

3x3 grid with scanning animation:
# Section 4 text
section4_text = Clip(
    asset=TextAsset(
        text=f"{SCENES_ANALYZED:,} scenes analyzed\n{FRAMES_ANALYZED:,} Frames Analyzed\n\nPure video intelligence.\nPowered by you.",
        font=Font(size=52, color="#FFFFFF", family="Arial Bold"),
    ),
    duration=5,
)
track.add_clip(25, section4_text)

# 3x3 grid with scanning effect
grid_positions = [
    (-0.4, -0.4), (0, -0.4), (0.4, -0.4),  # Top row
    (-0.4, 0), (0, 0), (0.4, 0),           # Middle row
    (-0.4, 0.4), (0, 0.4), (0.4, 0.4),     # Bottom row
]

for idx, (x_pos, y_pos) in enumerate(grid_positions):
    video = videos[idx % len(videos)]

    video_start = 50 + (idx % 10)
    if video_start > video.length - 10:
        video_start = 20

    video_clip = Clip(
        asset=VideoAsset(
            id=video.id,
            start=video_start,
            volume=0.1,
        ),
        duration=0.6,
        scale=0.28,
        fit="crop",
        offset=Offset(x=x_pos, y=y_pos),
        transition=Transition(in_="fade", duration=0.15),
        filter=Filter.contrast if idx % 2 == 0 else Filter.boost,
        opacity=0.9,
    )

    video_track.add_clip(start=24.5 + (idx * 0.08), clip=video_clip)

# Add cascading columns
num_columns = 2
clips_per_column = 8
for col in range(num_columns):
    x_pos = -0.3 + (col * 0.6)

    for row in range(clips_per_column):
        y_pos = -0.7 + (row * 0.23)
        clip_idx = col * clips_per_column + row

        video = videos[clip_idx % len(videos)]

        video_start = 40 + (clip_idx % 8)
        if video_start > video.length - 10:
            video_start = 25

        video_clip = Clip(
            asset=VideoAsset(
                id=video.id,
                start=video_start,
                volume=0.15,
            ),
            duration=0.3,
            scale=0.3,
            fit="crop",
            offset=Offset(x=x_pos, y=y_pos),
        )
        video_track.add_clip(start=25.7 + (row * 0.3) + (col * 0.1), clip=video_clip)

Step 9: Add Outro and Render

# Outro text
outro_text = Clip(
    asset=TextAsset(
        text="Ready for 2026? Build it.",
        font=Font(size=60, family="Roboto Bold")
    ),
    duration=2,
)
track.add_clip(31, outro_text)

# Add tracks to timeline
timeline.add_track(video_track)
timeline.add_track(track)

# Generate stream
stream_url = timeline.generate_stream()

What You Get

A professional video that:
  • Visualizes your video metrics in real-time
  • Uses 100+ of your actual videos in the composition
  • Creates dynamic animations and transitions
  • Pairs everything with music
  • Tells the story of your year in video data
Perfect for:
  • Marketing your platform
  • Celebrating milestones with users
  • Annual reports
  • Social media recap posts
  • Team celebrations
Here’s the final rendered recap video:

The Result

With this system, you transform dry analytics into engaging visual stories. Your users see not just numbers, but a cinematic journey through what you’ve accomplished together. It’s data visualization meets creative storytelling — all powered by code.

Explore the Full Notebook

Open the complete implementation with procedural animation logic, timing calculations, and advanced effects.