How It's Built
This demo site was built from create-next-app to production in a single session using Claude Code and deployed on Eyevinn Open Source Cloud.
The Team
| Role | Agent | Focus |
|---|---|---|
| Developer | Claude Opus 4 | Full-stack implementation, debugging, deployment |
| Human | birme | Architecture decisions, bug identification, OSC service management |
By the Numbers
2,965
Lines of code
22
Commits
28
Source files
6
OSC services
~5h
Wall-clock time
1
Session
From npx create-next-app to production deploy in a single afternoon.
Architecture
Browser │ ├─ Upload video ──────► MinIO (uploads bucket) │ (presigned URLs, │ │ 20MB chunks) │ │ ▼ ├─ POST /api/process ──► Pipeline │ │ │ ┌──────┴──────┐ │ ▼ ▼ │ eyevinn- eyevinn- │ ffmpeg-s3 ffmpeg-s3 │ (clip) (thumbnail) │ │ │ │ ▼ ▼ │ MinIO clips MinIO thumbnails │ │ │ ▼ │ eyevinn-auto-subtitles │ (Whisper / GPT-4o) │ │ │ ▼ │ MinIO subtitles + jobs │ │ ├─ GET /result/id ◄──┘ │ (video + VTT + metrics │ + standard scores) │ └─ Share link ──► OG image from thumbnail
Open Source Cloud Services
| Service | Purpose |
|---|---|
| eyevinn-web-runner | Hosts the Next.js app from a GitHub repo. Rebuilds on push. |
| eyevinn-auto-subtitles | AI transcription service. Accepts video URL, returns WebVTT via Whisper/GPT-4o. |
| eyevinn-ffmpeg-s3 | Ephemeral FFmpeg instances that read/write S3. Used for clip extraction and thumbnail generation. |
| minio-minio | S3-compatible object storage. 5 buckets: uploads, clips, subtitles, thumbnails, jobs, feedback. |
| eyevinn-app-config-svc | Parameter store for runtime config. Bypasses broken shell export of JWTs in web-runner entrypoint. |
| valkey-io-valkey | Backing store for app-config-svc. |
Features Delivered
- ✓Video upload with chunked multipart (20MB parts, presigned URLs direct to MinIO)
- ✓URL input for online videos
- ✓Video preview with scrubbing to select clip start position
- ✓3-minute clip extraction via eyevinn-ffmpeg-s3
- ✓AI transcription via eyevinn-auto-subtitles (Whisper-1 / GPT-4o)
- ✓WebVTT subtitle generation with download
- ✓In-browser video player with synced subtitle track
- ✓Quality scoring engine (CPS, CPL, timing, overlap analysis)
- ✓Side-by-side compliance scoring against Netflix, BBC, and EBU standards
- ✓Shareable result pages with 4-day retention
- ✓Dynamic OG images extracted from video for social sharing
- ✓Thumbnail extraction at 5s mark via FFmpeg
- ✓User feedback (thumbs up/down + comment) stored in MinIO
- ✓Automatic housekeeping of old clips/subtitles/thumbnails/jobs every hour
- ✓Job persistence to MinIO surviving app restarts
- ✓SEO: JSON-LD structured data, FAQ schema, robots.txt, sitemap, canonical URLs
- ✓Source file cleanup after clip extraction
- ✓14 language support
Key Debugging Moments
Shell-corrupted JWT tokens
The web-runner entrypoint shell export mangled the OSC access token (336 chars instead of 308). The config loader was set to skip already-set env vars. Fix: always override from app-config-svc.
FFmpeg can't detect .mp4 from presigned URLs
Presigned URLs have query parameters that obscure the file extension. FFmpeg couldn't auto-detect the output format. Went through three iterations: -f mp4, then frag_keyframe+empty_moov, then mpegts, before the upstream fix landed.
ffmpeg-s3 staging directory bug
The service's rewriteCmdString() replaced the S3 output URL with a presigned URL before the local staging path replacement could match it. Output went directly to S3 but the service expected it in a local directory. Filed issue #7; maintainer fixed it same day.
Auto-subtitles 401 Unauthorized
The transcription service requires a Service Access Token (SAT) via x-jwt: Bearer header, obtained from the OSC token endpoint. Initial implementation was missing this auth layer entirely.
Uppercase S3:// workaround that didn't work
Tried S3:// (uppercase) to bypass the presigned URL bug. It bypassed the check but toUrl() treated it as a file:// protocol. Upload to S3 silently failed. Waited for upstream fix instead.
CORS on direct MinIO uploads
Initially tried proxying uploads through the backend, but web-runner has a 5MB body limit. Switched to presigned URL pattern (browser uploads directly to MinIO) with chunked multipart, matching the STTV+ approach.
In-memory job loss on restart
Jobs were stored in a JavaScript Map and lost on every deploy. Shared result URLs would 404 after restart. Added MinIO-backed persistence for completed jobs while keeping the Map as a hot cache for active processing.
Tech Stack
Frontend
Next.js 16, React 19, Tailwind CSS 4
Backend
Next.js API Routes, TypeScript 5
Storage
MinIO (S3-compatible), AWS SDK
AI
OpenAI Whisper-1, GPT-4o Transcribe
Media Processing
FFmpeg via eyevinn-ffmpeg-s3
SDK
@osaas/client-core
Built with AI + Open Source
This entire application was built by a Claude Code agent with human guidance in a single session. The infrastructure runs entirely on open source services via Eyevinn Open Source Cloud.