An entry for the streamplace vod showcase
1# Streamhut VOD Bundle
2
3Video playback backend for Streamhut. Serves HLS playlists, thumbnails, and preview sprites.
4
5## Features
6
7- **HLS Proxying** - Rewrites stream.place playlists with absolute URLs
8- **Thumbnails** - Extracted from video at 3-second mark
9- **Preview Sprites** - 8-frame sprite sheets for hover preview
10- **VTT Coordinates** - WebVTT file mapping timestamps to sprite frames
11- **AppView Integration** - Optional connection to indexer for multi-creator support
12
13## Endpoints
14
15### Video Discovery
16
17| Method | Endpoint | Description |
18|--------|----------|-------------|
19| GET | `/listVideos` | List videos (from AppView or stream.place fallback) |
20| POST | `/getVideo` | Get single video metadata |
21| POST | `/getVideoStreams` | Get quality levels and audio tracks |
22
23### Playback
24
25| Method | Endpoint | Description |
26|--------|----------|-------------|
27| GET | `/getPlaylist` | HLS master playlist (browser-cacheable) |
28
29### Media Assets
30
31| Method | Endpoint | Description |
32|--------|----------|-------------|
33| GET | `/getThumbnail` | Video thumbnail JPEG |
34| GET | `/getSprite` | Preview sprite sheet JPEG |
35| GET | `/getVtt` | Sprite coordinate VTT |
36| GET | `/isMetadataReady` | Check if assets are generated |
37| POST | `/batchMetadataReady` | Batch check multiple videos |
38
39### Profiles
40
41| Method | Endpoint | Description |
42|--------|----------|-------------|
43| GET | `/getProfile` | Get creator profile from Bluesky |
44| POST | `/batchProfiles` | Batch fetch multiple profiles |
45
46### Social Sharing
47
48| Method | Endpoint | Description |
49|--------|----------|-------------|
50| GET | `/shareVideo` | Share page with OG meta tags for videos |
51| GET | `/shareCreator` | Share page with OG meta tags for creators |
52
53The share endpoints return HTML with Open Graph meta tags for rich link previews, then redirect to the frontend. Titles use the format "Streamhut | \<title\>".
54
55## Background Job
56
57**`syncAndProcessVideos`** - Runs every 10 minutes
58
591. Fetches video list (from AppView if configured, otherwise stream.place)
602. For each unprocessed video:
61 - Downloads master playlist
62 - Generates thumbnail via ffmpeg
63 - Generates sprite sheet and VTT
643. Caches everything to `/tmp/vod-cache/`
65
66## Configuration
67
68### Secrets
69
70| Name | Required | Description |
71|------|----------|-------------|
72| `APPVIEW_URL` | No | URL to AppView bundle for multi-creator support |
73
74### Without AppView
75
76Falls back to querying stream.place directly (single-creator mode).
77
78### With AppView
79
80```bash
81# Set the secret
82APPVIEW_URL=https://at-run.example.com/bundle/.../streamhut-appview/latest
83```
84
85Now `/listVideos` returns videos from all registered creators.
86
87## Cache Structure
88
89```
90/tmp/vod-cache/
91└── {sha256(uri)}/
92 ├── video.json # Video metadata
93 ├── master.m3u8 # Cached HLS playlist
94 ├── thumbnail.jpg # 480p thumbnail
95 ├── sprites.jpg # 4x2 sprite grid
96 └── sprites.vtt # Timestamp → sprite mapping
97```
98
99## Build & Deploy
100
101```bash
102# Build
103npm run bundle:vod
104
105# Deploy
106npm run deploy:vod
107
108# Deploy with version bump
109npm run deploy:minor
110npm run deploy:major
111```
112
113## Usage Examples
114
115### List Videos
116
117```bash
118curl "https://runner.example.com/bundle/.../atmosphereconf-vod/latest/listVideos?limit=10"
119```
120
121### Play Video
122
123```javascript
124import Hls from 'hls.js'
125
126const playlistUrl = `${RUNNER_URL}/bundle/.../atmosphereconf-vod/latest/getPlaylist?uri=${encodeURIComponent(videoUri)}`
127
128const hls = new Hls()
129hls.loadSource(playlistUrl)
130hls.attachMedia(videoElement)
131```
132
133### Show Thumbnail
134
135```html
136<img src="${RUNNER_URL}/bundle/.../atmosphereconf-vod/latest/getThumbnail?uri=${uri}" />
137```
138
139### Hover Preview
140
141```javascript
142// Fetch sprite VTT
143const vttUrl = `${RUNNER_URL}/bundle/.../getVtt?uri=${uri}`
144const vtt = await fetch(vttUrl).then(r => r.text())
145
146// Parse and use sprite coordinates for hover preview
147```
148
149## Files
150
151```
152apps/vod/
153├── src/
154│ ├── index.ts # Bundle manifest and endpoints
155│ ├── jobs.ts # Background sync job
156│ ├── ffmpeg.ts # FFmpeg utilities
157│ ├── thumbnail.ts # Thumbnail extraction
158│ └── vtt.ts # Sprite generation
159└── README.md
160```
161
162## Dependencies
163
164- **FFmpeg** - Required on runner for thumbnail/sprite generation
165- **stream.place** - Source of HLS video streams