Quick Example
import videodb
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("m-xxx")
# Get streaming URL
stream_url = video.generate_stream()
# Get thumbnail
thumbnail_url = video.generate_thumbnail()
# Get download link
download_url = video.download(name="sample video")
import { connect } from 'videodb';
const conn = connect();
const coll = await conn.getCollection();
const video = await coll.getVideo("m-xxx");
// Get streaming URL
const streamUrl = await video.generateStream();
// Get thumbnail URL
const thumbnailUrl = await video.generateThumbnail();
// Get download link
const downloadUrl = await video.download("sample video");
Embed Player
Basic HTML Embed
<video
id="player"
controls
width="100%"
poster="{thumbnail_url}"
>
<source
src="{stream_url}"
type="application/x-mpegURL"
>
</video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
const video = document.getElementById('player');
const src = video.querySelector('source').src;
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
}
</script>
React Component
import Hls from 'hls.js';
import { useRef, useEffect } from 'react';
function VideoPlayer({ streamUrl, thumbnailUrl, title }) {
const videoRef = useRef(null);
useEffect(() => {
const video = videoRef.current;
if (!video) return;
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(streamUrl);
hls.attachMedia(video);
return () => hls.destroy();
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Safari native HLS
video.src = streamUrl;
}
}, [streamUrl]);
return (
<video
ref={videoRef}
controls
poster={thumbnailUrl}
title={title}
style={{ width: '100%' }}
/>
);
}
Vue Component
<template>
<video
ref="videoPlayer"
controls
:poster="thumbnailUrl"
style="width: 100%"
/>
</template>
<script setup>
import Hls from 'hls.js';
import { ref, onMounted, onUnmounted } from 'vue';
const props = defineProps(['streamUrl', 'thumbnailUrl']);
const videoPlayer = ref(null);
let hls = null;
onMounted(() => {
if (Hls.isSupported()) {
hls = new Hls();
hls.loadSource(props.streamUrl);
hls.attachMedia(videoPlayer.value);
}
});
onUnmounted(() => {
if (hls) hls.destroy();
});
</script>
Embed Code Generation
get_embed_code()
Generate an HTML iframe embed string directly from SDK objects. Available on Video, Shot, SearchResult, Timeline, RTStream, RTStreamShot, RTStreamExportResult, and Editor Timeline.
embed_html = obj.get_embed_code(
width="100%", # iframe width
height=405, # iframe height in pixels
title="VideoDB Player", # iframe title attribute
allow_fullscreen=True, # allow fullscreen
auto_generate=True # auto-call generate_stream() if player_url missing
)
<iframe> string. Raises ValueError if player_url is not available and cannot be auto-generated.
Video Embed
video = coll.get_video("m-xxx")
# Auto-generates stream URL if needed
embed_html = video.get_embed_code()
print(embed_html)
# <iframe src="https://console.videodb.io/player?url=..." width="100%" height="405" ...></iframe>
# Custom dimensions
embed_html = video.get_embed_code(width="640px", height=360, title="My Video")
Search Result Embed
results = video.search("product demo")
# Embed the compiled search results
embed_html = results.get_embed_code(height=480)
RTStream Note
RTStream does not support auto_generate. You must call generate_stream(start, end) explicitly before calling get_embed_code():
rt_stream.generate_stream(start=0, end=120)
embed_html = rt_stream.get_embed_code(auto_generate=False)
build_iframe_embed_code() Utility
A standalone helper when you already have a player URL:
from videodb import build_iframe_embed_code
embed_html = build_iframe_embed_code(player_url, width="100%", height=405)
Thumbnail Generation
Get Video Thumbnail
# Default thumbnail (first frame)
thumbnail_url = video.generate_thumbnail()
# Thumbnail at specific time
thumbnail_url = video.generate_thumbnail(time=30.5)
// Default thumbnail (first frame)
const thumbnailUrl = await video.generateThumbnail();
// Thumbnail at specific time
const thumbnailUrl = await video.generateThumbnail(30.5);
Multiple Thumbnails for Preview
def generate_preview_thumbnails(video, count=5):
"""Generate evenly spaced thumbnails across video"""
duration = video.duration
interval = duration / (count + 1)
thumbnails = []
for i in range(1, count + 1):
time = interval * i
url = video.generate_thumbnail(time=time)
thumbnails.append({
"time": time,
"url": url
})
return thumbnails
previews = generate_preview_thumbnails(video, count=5)
async function generatePreviewThumbnails(video, count = 5) {
const duration = video.duration;
const interval = duration / (count + 1);
const thumbnails = [];
for (let i = 1; i <= count; i++) {
const time = interval * i;
const url = await video.generateThumbnail(time);
thumbnails.push({ time, url });
}
return thumbnails;
}
const previews = await generatePreviewThumbnails(video, 5);
Share Links
VideoDB Console Player
Generate shareable links using the console player:import urllib.parse
stream_url = video.generate_stream()
encoded_url = urllib.parse.quote(stream_url, safe='')
share_link = f"https://console.videodb.io/player?url={encoded_url}"
const streamUrl = await video.generateStream();
const encodedUrl = encodeURIComponent(streamUrl);
const shareLink = `https://console.videodb.io/player?url=${encodedUrl}`;
Share Specific Clips
# Share a specific segment
clip_url = video.generate_stream([(120, 180)])
share_clip = f"https://console.videodb.io/player?url={urllib.parse.quote(clip_url)}"
// Share a specific segment
const clipUrl = await video.generateStream([[120, 180]]);
const shareClip = `https://console.videodb.io/player?url=${encodeURIComponent(clipUrl)}`;
Social Media Metadata
Open Graph Tags
<!-- Video metadata for social sharing -->
<meta property="og:type" content="video.other" />
<meta property="og:title" content="{video_title}" />
<meta property="og:description" content="{video_description}" />
<meta property="og:image" content="{thumbnail_url}" />
<meta property="og:video" content="{stream_url}" />
<meta property="og:video:type" content="application/x-mpegURL" />
<meta property="og:video:width" content="1920" />
<meta property="og:video:height" content="1080" />
Twitter Card
<meta name="twitter:card" content="player" />
<meta name="twitter:title" content="{video_title}" />
<meta name="twitter:description" content="{video_description}" />
<meta name="twitter:image" content="{thumbnail_url}" />
<meta name="twitter:player" content="{embed_url}" />
<meta name="twitter:player:width" content="1280" />
<meta name="twitter:player:height" content="720" />
Generate Metadata Object
def generate_social_metadata(video, title, description):
"""Generate metadata for social sharing"""
stream_url = video.generate_stream()
thumbnail_url = video.generate_thumbnail()
return {
"title": title,
"description": description,
"thumbnail": thumbnail_url,
"video_url": stream_url,
"og": {
"type": "video.other",
"title": title,
"description": description,
"image": thumbnail_url,
"video": stream_url
},
"twitter": {
"card": "player",
"title": title,
"description": description,
"image": thumbnail_url
}
}
async function generateSocialMetadata(video, title, description) {
const streamUrl = await video.generateStream();
const thumbnailUrl = await video.generateThumbnail();
return {
title,
description,
thumbnail: thumbnailUrl,
videoUrl: streamUrl,
og: {
type: "video.other",
title,
description,
image: thumbnailUrl,
video: streamUrl
},
twitter: {
card: "player",
title,
description,
image: thumbnailUrl
}
};
}
CDN Delivery
Stream URL Structure
VideoDB serves content through a global CDN:https://stream.videodb.io/v3/published/manifests/{manifest-id}.m3u8
| Endpoint | Purpose |
|---|---|
stream.videodb.io | HLS streaming |
cdn.videodb.io | Direct file downloads |
URL Caching Strategy
import time
from functools import lru_cache
class VideoURLCache:
def __init__(self, ttl=23 * 3600): # 23 hours
self.cache = {}
self.ttl = ttl
def get_stream_url(self, video, timestamps=None):
key = f"{video.id}:{timestamps}"
if key in self.cache:
url, expires = self.cache[key]
if time.time() < expires:
return url
# Generate new URL
if timestamps:
url = video.generate_stream(timestamps)
else:
url = video.generate_stream()
self.cache[key] = (url, time.time() + self.ttl)
return url
# Usage
url_cache = VideoURLCache()
stream_url = url_cache.get_stream_url(video)
class VideoURLCache {
constructor(ttl = 23 * 60 * 60 * 1000) { // 23 hours
this.cache = new Map();
this.ttl = ttl;
}
async getStreamUrl(video, timestamps = null) {
const key = `${video.id}:${JSON.stringify(timestamps)}`;
if (this.cache.has(key)) {
const { url, expires } = this.cache.get(key);
if (Date.now() < expires) {
return url;
}
}
// Generate new URL
const url = timestamps
? await video.generateStream(timestamps)
: await video.generateStream();
this.cache.set(key, {
url,
expires: Date.now() + this.ttl
});
return url;
}
}
// Usage
const urlCache = new VideoURLCache();
const streamUrl = await urlCache.getStreamUrl(video);
Player Features
Autoplay with Mute
<video
id="player"
autoplay
muted
playsinline
poster="{thumbnail_url}"
>
<source src="{stream_url}" type="application/x-mpegURL">
</video>
Loop Playback
<video id="player" loop controls>
<source src="{stream_url}" type="application/x-mpegURL">
</video>
Custom Controls
function CustomPlayer({ streamUrl }) {
const videoRef = useRef(null);
const [playing, setPlaying] = useState(false);
const [progress, setProgress] = useState(0);
const togglePlay = () => {
if (playing) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setPlaying(!playing);
};
return (
<div className="player-container">
<video
ref={videoRef}
onTimeUpdate={(e) => {
const pct = (e.target.currentTime / e.target.duration) * 100;
setProgress(pct);
}}
/>
<div className="controls">
<button onClick={togglePlay}>
{playing ? 'Pause' : 'Play'}
</button>
<div className="progress-bar" style={{ width: `${progress}%` }} />
</div>
</div>
);
}
Responsive Embed
Aspect Ratio Container
.video-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
}
.video-container video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
<div class="video-container">
<video controls>
<source src="{stream_url}" type="application/x-mpegURL">
</video>
</div>
Multiple Aspect Ratios
.video-16-9 { padding-bottom: 56.25%; }
.video-4-3 { padding-bottom: 75%; }
.video-1-1 { padding-bottom: 100%; }
.video-9-16 { padding-bottom: 177.78%; }
Best Practices
| Practice | Reason |
|---|---|
| Cache stream URLs | Avoid regenerating on every request |
| Use thumbnails | Improve perceived load time |
| Preload metadata | preload="metadata" for faster starts |
| Lazy load off-screen | Defer loading until visible |
| Handle errors | Show fallback on stream failure |
Error Handling
function VideoPlayer({ streamUrl, fallbackUrl }) {
const [error, setError] = useState(false);
if (error && fallbackUrl) {
return <img src={fallbackUrl} alt="Video unavailable" />;
}
return (
<video
controls
onError={() => setError(true)}
>
<source src={streamUrl} type="application/x-mpegURL" />
</video>
);
}
Next Steps
Streams and Exports
Generate clips and export video
Timeline Architecture
Compose video programmatically