From b4c81d820c6ccf0da68de4c38e42ef788e888bde Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 15 Sep 2026 10:13:12 +0200 Subject: [PATCH] Cast a party's video to a Chromecast Implements a detailed spec (Google Cast / Chromecast integration) asking for a member to cast the party's video to a Chromecast's Default Media Receiver, with play/pause/seek shown as a remote on the sender's own screen. The spec's description of this app's architecture (one shared Emby stream per party, the server proxying 100% of Emby access) was verified directly against the real code before building anything, since it named specific existing services/routes as the integration points. A Chromecast is a new kind of client for the existing party-stream/burn relay: it can't carry the sender's httpOnly session cookie at all, so it needed its own narrow way in without weakening "the server holds the only real Emby token" the rest of the app is built around. - New CastTokenService (same shape as AuthService: in-memory Map, opaque crypto.randomUUID() keys, TTL-checked) issues a token scoped to one {partyId, userId}, standing in for that member's own EmbySession. New POST/DELETE /api/parties/:partyId/cast-token to mint/revoke one. - The 4 routes a Chromecast actually needs (party-stream/master.m3u8, party-stream/raw, burn/master.m3u8, burn/:segment) gained one small fallback (sessionForStream in index.ts): try the session cookie first, same as ever, then a castToken query param - either way the result is the same EmbySession type, so PartyStreamService/TranscodeService/ EmbyClient needed zero changes. The image/subtitle routes stay cookie-only, outside the spec's own 4-route list. - PartyService.leave() now revokes whatever cast token the leaving member held, unconditionally - a token dies the instant its owner actually leaves the party regardless of TTL or whether the client got a chance to call the explicit stop-casting route. - New client/src/cast.ts is the only module touching the CAF Sender SDK (loaded as a pinned CDN script, same pattern as hls.js/socket.io) - exposes a small PlaybackDriver interface (currentTime/paused/play/ pause) with two implementations, one over the local