moqbox#
Shared media rooms. Paste a URL, everyone watches together.
Requires ffmpeg and yt-dlp on PATH.
TODO#
- Gifs in chat
- Improved UX
- Perhaps playlist / history should be somewhere else?
- Need space for gifs
- Improve over the default player (audio level normalization?)
- Fix player card layering / layouting
- Built-in search?
- Track handling
- Better caching
- Cleaner file-system backend (s3 at some point?)
- Resolving a link that we have the file for should not re-resolve
- User management
- ATproto login
- Anon "login" (pick a username -- server-side)
- Nice OG images of a room (name + most played video thumbnail, title, a few active members?)
- Direct track upload
- Reorder up-next tracks
- Shuffle up-next tracks
- Publish atmo.rsvp events for sharing moments together
- Actual MOQ transport layer for chromium users / polyfill?
- ACL / private rooms? (restrict to a set of dids?)
Shipped#
- Named rooms with
/r/{name}URLs (create_roomreturns the name; the API resolves name → UUID internally) - Per-room autoplay + skip-threshold settings (room settings panel in the playlist section)
- Vote skip (replace
skipwithvote_skip; threshold-based, default 34%) - Auto-play triggers on client connect (in addition to the idle sweep)
Quick Start#
# Backend
cargo run
# Open http://localhost:3000
# Or with the SolidJS dev server (hot reload):
cd frontend && npm install && npm run dev
# Opens http://localhost:5173, proxies /api/* to backend
The built frontend (SolidJS SPA) is pre-committed to static/dist/, so
cargo run alone works. If you're hacking on the frontend, use the Vite dev
server from frontend/.
Configuration#
| Flag | Env Variable | Default | Description |
|---|---|---|---|
--http-addr |
MOQBOX_HTTP_ADDR |
0.0.0.0:3000 |
HTTP listen address |
--cache-dir |
MOQBOX_CACHE_DIR |
/tmp/moqbox |
yt-dlp download dir |
--db-path |
MOQBOX_DB_PATH |
moqbox.db |
SQLite database file |
Architecture#
One Axum binary serving a REST API, WebSocket state/chat, and HTTP Range media serving on the same port. Per-room mpsc actor processes commands sequentially. Room state persisted to SQLite (WAL mode).
graph TB
Browser[SolidJS SPA] -->|REST + WS| Axum[Axum Server]
Browser -->|HTTP Range| Axum
Axum -->|commands| Registry[Room Registry]
Registry -->|spawn| Actor[Room Actor]
Actor -->|effects| Store[SQLite Store]
Actor -->|spawn| Download[Download Task]
Download -->|yt-dlp| Media[Media File on Disk]
Actor -->|publish| WS[WebSocket Clients]
Perpetual playlist#
Rooms have a persistent playlist. When the queue is empty and the room is idle, the next playlist entry is auto-filled. Entries are pre-resolved (downloaded) at add-time so auto-played tracks never show "Loading...".