Table of Contents
Web Media Player
Custom video player with HLS playback, synchronized transcript sidebar, and keyboard shortcuts. Added in M020/S01.
Architecture
┌──────────────────────────────────────────────────────────┐
│ WatchPage (/watch/:videoId) │
│ │
│ ┌─────────────────────┐ ┌────────────────────────────┐ │
│ │ VideoPlayer │ │ TranscriptSidebar │ │
│ │ ┌────────────────┐ │ │ Segments sorted by time │ │
│ │ │ <video> elem │ │ │ Binary search for active │ │
│ │ └────────────────┘ │ │ Auto-scroll on playback │ │
│ │ PlayerControls │ │ Click → seek to timestamp │ │
│ └──────────┬───────────┘ └──────────┬─────────────────┘ │
│ │ useMediaSync() │ │
│ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
Video Source Detection
Three-path fallback for maximum browser compatibility:
- HLS via hls.js — Lazy-loaded (dynamic import). Used when
Hls.isSupported()returns true (Chrome, Firefox, Edge) - Safari native HLS —
<video>element handles.m3u8natively on Safari/iOS - Direct MP4 fallback — Plain
<video src="...">for non-HLS sources
hls.js is loaded only when needed (~200KB), keeping the main bundle small.
Synchronized Transcript
The TranscriptSidebar component displays timestamped transcript segments synchronized to video playback:
- Active segment detection: Binary search (O(log n)) on sorted segments by
start_time - Auto-scroll: Active segment scrolls into view with
scrollIntoView({ block: "center" }) - Click to seek: Each segment is a
<button>for keyboard accessibility. Click seeks the video to that timestamp - Non-blocking: Transcript fetch failure doesn't break the player — degrades gracefully
useMediaSync Hook
Shared playback state between VideoPlayer and TranscriptSidebar:
currentTime(number) — Current playback position in secondsduration(number) — Total video durationisPlaying(boolean) — Playback stateseekTo(time)(function) — Seek video to specific time
Keyboard Shortcuts
- Space — Play/pause
- ← / → — Seek ±5 seconds
- Shift+← / Shift+→ — Seek ±10 seconds
- ↑ / ↓ — Volume ±10%
- M — Mute/unmute
- F — Toggle fullscreen
- 1-9 — Seek to 10%-90%
Speed Controls
Playback rate options: 0.5x, 0.75x, 1x, 1.25x, 1.5x, 2x.
API Endpoints
GET /api/v1/videos/{id}— Video metadata with creator info (SourceVideoDetail)GET /api/v1/videos/{id}/transcript— Ordered transcript segments (TranscriptForPlayerResponse)
Integration Points
- TechniquePage: Key moment timestamps are clickable links to
/watch/:videoId?t=<seconds> - Code-splitting: WatchPage is lazy-loaded via
React.lazy+Suspense
Key Files
frontend/src/pages/WatchPage.tsx— Page componentfrontend/src/components/VideoPlayer.tsx— Video element + HLS setupfrontend/src/components/PlayerControls.tsx— Play/pause, speed, volume, seek barfrontend/src/components/TranscriptSidebar.tsx— Synchronized transcript displayfrontend/src/components/AudioWaveform.tsx— Waveform visualization for audio content (M021/S05)frontend/src/components/ChapterMarkers.tsx— Seek bar chapter overlay (M021/S05)frontend/src/hooks/useMediaSync.ts— Shared playback state hookbackend/routers/videos.py— Video detail + transcript API
See also: Architecture, API-Surface, Frontend with chapter title
- Click seeks playback to chapter start time
- Integrated into
PlayerControlsvia wrapper container div
Audio Waveform (M021/S05)
AudioWaveform component renders when content is audio-only (no video_url):
- Hidden
<audio>element shared betweenuseMediaSyncand WaveSurfer - wavesurfer.js with MediaElement backend — playback controlled identically to video mode
- Dark-themed CSS matching the video player area
- RegionsPlugin for labeled chapter regions with drag/resize support
Dependencies
wavesurfer.js— waveform rendering (~200KB, loaded only in audio mode)useMediaSynchook widened fromHTMLVideoElementtoHTMLMediaElementfor audio/video polymorphism
Key Moment Timeline Pins (M024/S02)
ChapterMarkers upgraded from thin 3px line ticks to 12px color-coded circle pins.
Pin Colors by Content Type
| Content Type | CSS Custom Property | Color |
|---|---|---|
| technique | --color-pin-technique |
Cyan |
| settings | --color-pin-settings |
Amber |
| reasoning | --color-pin-reasoning |
Purple |
| workflow | --color-pin-workflow |
Green |
Active State
When playback is within a key moment's time range, the corresponding pin scales to 1.3x. PlayerControls passes currentTime to ChapterMarkers for active detection.
Touch Targets
::before pseudo-element with inset:-6px provides touch-friendly hit areas without enlarging the visual pin.
Tooltips
Enriched: title + formatted time range + content type label.
Inline Player on Technique Pages (M024/S02)
Collapsible video player on TechniquePage, positioned between summary and body sections.
- Collapse/expand animation — CSS
grid-template-rows: 0fr/1frtransition - Multi-source-video selector — dropdown for technique pages sourced from multiple videos
- Chapter pins — key moments rendered as pin markers on the inline seek bar
- Bibliography seek wiring — time links render as seek buttons (calling
mediaSync.seekTo) when the inline player is active for the matching video, or as<Link>to WatchPage otherwise - Uses existing
useMediaSynchook,VideoPlayer, andPlayerControlscomponents
Embed Player (M024/S03)
Chrome-free player at /embed/:videoId for iframe embedding.
- No header/nav/footer — route registered at top-level
RoutesinApp.tsxbefore AppShell catch-all - Content-type-aware height — video: 405px, audio-only: 120px
- "Powered by Chrysopedia" branding — link opens origin in new tab with
rel="noopener" - Dark full-viewport layout — matches existing player theme
- Files:
EmbedPlayer.tsx,EmbedPlayer.module.css
Key Files
frontend/src/pages/WatchPage.tsx— Page componentfrontend/src/components/VideoPlayer.tsx— Video element + HLS setupfrontend/src/components/PlayerControls.tsx— Play/pause, speed, volume, seek bar, currentTime pass-through to ChapterMarkersfrontend/src/components/ChapterMarkers.tsx— 12px color-coded circle pins with active state and touch targets (M024/S02)frontend/src/components/TranscriptSidebar.tsx— Synchronized transcript displayfrontend/src/components/AudioWaveform.tsx— Waveform visualization for audio content (M021/S05)frontend/src/pages/EmbedPlayer.tsx— Chrome-free embed player (M024/S03)frontend/src/pages/TechniquePage.tsx— Inline collapsible player with bibliography seek (M024/S02)frontend/src/hooks/useMediaSync.ts— Shared playback state hookbackend/routers/videos.py— Video detail + transcript API
See also: Architecture, API-Surface, Frontend
Chrysopedia Wiki
Architecture
Features
- Chat-Engine
- Search-Retrieval
- Highlights
- Personality-Profiles
- Posts (via Post Editor)
Reference
Operations