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

# Annual Video Statistics Recap

> Transform video analytics into a cinematic recap video with AI-powered visualizations

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

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

```bash theme={null}
pip install videodb
```

### Connect to VideoDB

```python theme={null}
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

```python theme={null}
# 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

```python theme={null}
# 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:

```python theme={null}
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:

```python theme={null}
# 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:

```python theme={null}
# 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:

```python theme={null}
# 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:

```python theme={null}
# 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:

```python theme={null}
# 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

```python theme={null}
# 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:

<iframe className="w-full aspect-video rounded-xl" src="https://console.videodb.io/player?url=https://d1zudc7ewmc6ey.cloudfront.net/v1/a0f7b1cd-26b1-4c1b-b2d8-deaef0be5524.m3u8" title="Annual Video Statistics Recap" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

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

<Card icon="notebook" title="Explore the Full Notebook" href="https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/editor/creative/2025_in_frames.ipynb">
  Open the complete implementation with procedural animation logic, timing calculations, and advanced effects.
</Card>

## Related Tutorials

<CardGroup cols={2}>
  <Card title="Faceless Video Creator" icon="video" href="/examples-and-tutorials/content-factory/faceless-video-creator">
    Generate complete videos from scripts
  </Card>

  <Card title="TikTok Lyric Videos" icon="music" href="/examples-and-tutorials/content-factory/tiktok-lyric-video">
    Auto-sync lyrics to music with animations
  </Card>
</CardGroup>
