CallMeVerity 2f62e68688 Stream video through backend with Range request support
Previously videos were served via direct RustFS URLs, which meant:
- No HTTP Range support (browsers had to download the entire file)
- Large videos couldn't play at all
- Video player rendered broken

Now videos stream through GET /api/videos/:id/stream which:
- Proxies video data from RustFS to the browser
- Supports Range requests (HTTP 206 Partial Content) for seeking
- Sets proper headers (Accept-Ranges, Content-Range, Content-Type)
- Caches for 24 hours

Frontend changes:
- VideoPlayer: added playsInline, preload=metadata, object-contain, error state
- VideoDetail: removed duplicate inline video, now uses VideoPlayer component
- index.css: style WebKit video controls (dark panel, no border-radius)
2026-06-03 03:56:39 +01:00
2026-06-03 00:44:48 +01:00
2026-06-03 00:44:48 +01:00
2026-06-03 00:44:48 +01:00

surf.nathan.rip

Momentum Mod surf replay hosting - video + .mtv replay files with automatic map tier badges, stage splits, and PB history.

Architecture

Layer Stack
Frontend React + Vite + Tailwind CSS v4
Backend ElysiaJS (Bun)
Storage RustFS (S3-compatible)
Database SQLite

How it works

  1. Upload a video + .mtv replay file via the auth-gated upload page
  2. Backend parses the .mtv binary header for metadata (map, player, time, Steam ID, stage splits)
  3. Map tier and thumbnail are fetched from the Momentum Mod API
  4. Video uploads go directly to RustFS via presigned URLs (no size limit)
  5. If a better time is uploaded for the same map+player, the PB is updated and the old run is archived as a previous PB

Setup

# Backend
cd backend
cp .env.example .env  # edit with your credentials
bun install
bun run dev

# Frontend
cd frontend
bun install
bun run dev

The frontend dev server proxies /api to localhost:3001.

Environment variables

Variable Required Description
ADMIN_TOKEN Yes Bearer token for upload/delete auth
RUSTFS_ENDPOINT No S3 endpoint (default: http://localhost:9000)
RUSTFS_ACCESS_KEY No S3 access key
RUSTFS_SECRET_KEY No S3 secret key
RUSTFS_BUCKET No S3 bucket name (default: surf)
RUSTFS_PUBLIC No Public read access (default: true)
PORT No Server port (default: 3001)
DB_PATH No SQLite path (default: ./data/surf.db)
STATIC_DIR No Frontend static files (default: ./public)

RustFS setup

  1. Create a bucket called surf in your RustFS instance
  2. Set bucket to public read for direct video URLs
  3. Configure CORS on the bucket to allow PUT from your frontend origin (required for presigned video uploads)
  4. Create an IAM policy with s3:ListBucket, s3:GetObject, s3:PutObject, s3:DeleteObject on surf/*

.mtv file format

195-byte binary header followed by JSON stats and LZMA-compressed replay data.

Offset Size Field
0x00 4 Magic (MMTV = 0x56544D4D)
0x04 4 Version
0x10 64 Map name (null-terminated)
0x50 41 Map hash
0x7B 4 Tick interval (float)
0x7F 8 Steam ID (uint64)
0x87 32 Player name (null-terminated)
0xA9 8 Run time (double)
0xB1 4 Total ticks
S
Description
No description provided
Readme 298 KiB
Languages
TypeScript 98.7%
CSS 0.5%
Dockerfile 0.4%
HTML 0.4%