watchparty/README.md
Nicolas b6818655b1
All checks were successful
Publier l'image Docker / publish (push) Successful in 6s
Proxy all Emby access through our own backend; drop server-url field; use Emby's real theme colors
Three changes requested:

1. Server proxies Emby, browser never talks to it directly. Login is
   now POST /api/auth/login {username,password} on our own backend;
   the backend calls Emby's AuthenticateByName server-side and mints
   its own opaque session (httpOnly cookie), storing
   {embyToken,userId,userName} in memory. The browser never sees or
   holds a real Emby access token. Every other call - search, item
   details, images, HLS streaming, party actions - goes to our own
   /api/* origin, authenticated by that cookie; server/src/embyProxy.js
   forwards to Emby server-side and streams the response back
   (including a playlist-rewrite pass for HLS .m3u8 so hls.js never
   gets handed a URL pointing at Emby directly). This also sidesteps
   the CORS problem the previous direct-to-Emby design had.

2. EMBY_SERVER_URL is backend-only config (.env.example); the login
   form no longer asks for a server address at all.

3. client/css/app.css now uses colors read directly off the live Emby
   server's own dark theme (--theme-primary-color-* etc, fetched via a
   JS probe against the running client, not guessed): accent green
   #52b54b/#6ccf65, near-black background #151216.

Tested for real: ran the server locally against the actual
EMBY_SERVER_URL and submitted intentionally-wrong credentials through
the UI - confirmed the full round trip (browser -> our backend -> real
Emby -> real 401 -> our error message) with no CORS issue, since it's
all server-to-server now. Login itself hasn't been exercised with real
credentials, so catalog/streaming remain unverified - see
docs/architecture.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 21:36:26 +02:00

98 lines
3.9 KiB
Markdown

# Watch Party for Emby
A standalone web app for watching the same title, in sync, with other users
of the same Emby server. It reads your Emby catalog and streams your Emby
media, but it's its own app with its own player — not an Emby plugin.
See [docs/architecture.md](docs/architecture.md) for the full design,
including why this replaced an earlier Emby-plugin attempt (still in
[src/WatchParty/](src/WatchParty/) for reference — see below).
## How it works
1. Sign in with your Emby account (just username/password — the server
address is fixed server-side, see Deploying below).
2. Search your Emby catalog for a title.
3. Start a party (you become the host) or join one with a 6-character code.
4. The host's play/pause/seek drives everyone else's player automatically.
Everything — catalog, images, video, sync — flows through this app's own
backend, which proxies whatever it needs from Emby server-side. The browser
never talks to Emby directly and never holds an Emby access token; it only
ever has a session cookie for this app.
## Status
Login has been exercised end to end against the real Emby server (with
intentionally-wrong credentials, to confirm the round trip without needing
real ones) — see [docs/architecture.md](docs/architecture.md#assumptions-to-verify)
for exactly what that confirms and what's still unverified beyond it
(mainly: HLS playback once actually logged in).
## Project layout
```
server/ Node.js backend: proxies ALL Emby access (auth, catalog,
images, HLS streaming) and coordinates party/room state +
realtime sync. The browser only ever talks to this.
src/index.js Express app + REST endpoints
src/embyAuth.js server-side login against Emby + session cookies
src/embyProxy.js generic Emby request/response proxying (incl. HLS)
src/wsHandler.js WebSocket protocol (see comments in the file)
src/partyManager.js in-memory room state
client/ Static frontend (plain JS, ES modules, no build step).
Talks only to server/ - never to Emby directly.
js/embyApi.js calls our own /api/* routes (auth, search, item, stream URL)
js/party.js our own backend's party REST + WebSocket client
js/views/ one file per screen (login, browse, item, player)
src/WatchParty/ The earlier Emby-plugin attempt. Builds fine and installs,
but Emby has no supported extension point for a player
button, and even menu integration required reverse-
engineering undocumented internals - see
docs/architecture.md for the full story. Kept for
reference; not the active direction.
docs/architecture.md full design doc for the standalone app
```
## Running locally
```bash
cd server
npm install
EMBY_SERVER_URL=https://your-emby-server npm start
```
Then open `http://localhost:3000`.
## Deploying
Build the Docker image from the **repo root** (it needs both `server/` and
`client/`):
```bash
docker build -f server/Dockerfile -t watchparty .
docker run -d --name watchparty -p 3000:3000 -e EMBY_SERVER_URL=https://theater.media.kyuno.fr watchparty
```
Then put it behind your existing reverse proxy like everything else
(`theater.media.kyuno.fr`, `git.dev.kyuno.fr`, ...), e.g.
`watchparty.kyuno.fr` → `http://<container>:3000`.
## Roadmap
- [x] Design + scaffold the standalone app
- [x] Smoke-test locally (routing, error handling) without a real Emby server
- [ ] Verify against the real Emby server (CORS is the main risk - see above)
- [ ] Try a full party end-to-end with two accounts
- [ ] TV show navigation (seasons/episodes) beyond flat search
- [ ] Tune transcoding target / support direct play when possible
No chat, no OIDC — deliberately out of scope (Emby-only auth was the
explicit choice; see docs/architecture.md).
## License
MIT — see [LICENSE](LICENSE).