An entry for the streamplace vod showcase
Streamhut VOD Bundle#
Video playback backend for Streamhut. Serves HLS playlists, thumbnails, and preview sprites.
Features#
- HLS Proxying - Rewrites stream.place playlists with absolute URLs
- Thumbnails - Extracted from video at 3-second mark
- Preview Sprites - 8-frame sprite sheets for hover preview
- VTT Coordinates - WebVTT file mapping timestamps to sprite frames
- AppView Integration - Optional connection to indexer for multi-creator support
Endpoints#
Video Discovery#
| Method | Endpoint | Description |
|---|---|---|
| GET | /listVideos |
List videos (from AppView or stream.place fallback) |
| POST | /getVideo |
Get single video metadata |
| POST | /getVideoStreams |
Get quality levels and audio tracks |
Playback#
| Method | Endpoint | Description |
|---|---|---|
| GET | /getPlaylist |
HLS master playlist (browser-cacheable) |
Media Assets#
| Method | Endpoint | Description |
|---|---|---|
| GET | /getThumbnail |
Video thumbnail JPEG |
| GET | /getSprite |
Preview sprite sheet JPEG |
| GET | /getVtt |
Sprite coordinate VTT |
| GET | /isMetadataReady |
Check if assets are generated |
| POST | /batchMetadataReady |
Batch check multiple videos |
Profiles#
| Method | Endpoint | Description |
|---|---|---|
| GET | /getProfile |
Get creator profile from Bluesky |
| POST | /batchProfiles |
Batch fetch multiple profiles |
Social Sharing#
| Method | Endpoint | Description |
|---|---|---|
| GET | /shareVideo |
Share page with OG meta tags for videos |
| GET | /shareCreator |
Share page with OG meta tags for creators |
The share endpoints return HTML with Open Graph meta tags for rich link previews, then redirect to the frontend. Titles use the format "Streamhut | <title>".
Background Job#
syncAndProcessVideos - Runs every 10 minutes
- Fetches video list (from AppView if configured, otherwise stream.place)
- For each unprocessed video:
- Downloads master playlist
- Generates thumbnail via ffmpeg
- Generates sprite sheet and VTT
- Caches everything to
/tmp/vod-cache/
Configuration#
Secrets#
| Name | Required | Description |
|---|---|---|
APPVIEW_URL |
No | URL to AppView bundle for multi-creator support |
Without AppView#
Falls back to querying stream.place directly (single-creator mode).
With AppView#
# Set the secret
APPVIEW_URL=https://at-run.example.com/bundle/.../streamhut-appview/latest
Now /listVideos returns videos from all registered creators.
Cache Structure#
/tmp/vod-cache/
└── {sha256(uri)}/
├── video.json # Video metadata
├── master.m3u8 # Cached HLS playlist
├── thumbnail.jpg # 480p thumbnail
├── sprites.jpg # 4x2 sprite grid
└── sprites.vtt # Timestamp → sprite mapping
Build & Deploy#
# Build
npm run bundle:vod
# Deploy
npm run deploy:vod
# Deploy with version bump
npm run deploy:minor
npm run deploy:major
Usage Examples#
List Videos#
curl "https://runner.example.com/bundle/.../atmosphereconf-vod/latest/listVideos?limit=10"
Play Video#
import Hls from 'hls.js'
const playlistUrl = `${RUNNER_URL}/bundle/.../atmosphereconf-vod/latest/getPlaylist?uri=${encodeURIComponent(videoUri)}`
const hls = new Hls()
hls.loadSource(playlistUrl)
hls.attachMedia(videoElement)
Show Thumbnail#
<img src="${RUNNER_URL}/bundle/.../atmosphereconf-vod/latest/getThumbnail?uri=${uri}" />
Hover Preview#
// Fetch sprite VTT
const vttUrl = `${RUNNER_URL}/bundle/.../getVtt?uri=${uri}`
const vtt = await fetch(vttUrl).then(r => r.text())
// Parse and use sprite coordinates for hover preview
Files#
apps/vod/
├── src/
│ ├── index.ts # Bundle manifest and endpoints
│ ├── jobs.ts # Background sync job
│ ├── ffmpeg.ts # FFmpeg utilities
│ ├── thumbnail.ts # Thumbnail extraction
│ └── vtt.ts # Sprite generation
└── README.md
Dependencies#
- FFmpeg - Required on runner for thumbnail/sprite generation
- stream.place - Source of HLS video streams