# 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 | \". ## Background Job **`syncAndProcessVideos`** - Runs every 10 minutes 1. Fetches video list (from AppView if configured, otherwise stream.place) 2. For each unprocessed video: - Downloads master playlist - Generates thumbnail via ffmpeg - Generates sprite sheet and VTT 3. 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 ```bash # 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 ```bash # 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 ```bash curl "https://runner.example.com/bundle/.../atmosphereconf-vod/latest/listVideos?limit=10" ``` ### Play Video ```javascript 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 ```html ``` ### Hover Preview ```javascript // 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