From ef70da1806e75f6750a3ecc03da99d498319f310 Mon Sep 17 00:00:00 2001 From: Jonathan Sykes Date: Sat, 3 Oct 2026 20:24:10 +0800 Subject: [PATCH] Document device media paths and direct transfer requirements --- docs/p2p-transfer.md | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/p2p-transfer.md diff --git a/docs/p2p-transfer.md b/docs/p2p-transfer.md new file mode 100644 index 0000000..c3c1d8c --- /dev/null +++ b/docs/p2p-transfer.md @@ -0,0 +1,69 @@ +# Direct device media transfers + +## Existing paths (audit before implementation) + +| Path | Existing media-byte route | Relevant code | +| --- | --- | --- | +| Paired remote | Controls only; the host plays its own source. No existing file-send action. | `frontend/app.js` Remote; `server/remote.js` | +| Presenter and OBS | Lyrics, timing and metadata only; neither currently plays host media. | Presenter and overlay modules; remote relay | +| Watch Party playback | Each guest loads independently through normal server/YouTube playback, unless already saved locally. | Party `applyState`, `Player.loadVideo`; `server/party.js` | +| Watch Party voice | Direct WebRTC audio; server forwards SDP/ICE only. | Party voice mesh, party `rtc` relay | +| On other devices / failed playback | Existing verified-file DataChannel download, 64 KiB frames, SHA-256 validation, worker writes OPFS. Only offered after server failure in playback. | `p2p-transfer.js`, `p2p-recv-worker.js`, `getFromPeers` | +| Save / preload | Server download first, then OPFS. | `preload`, `API.cacheDownload`, download endpoints | +| Export to Files / Photos | Local OPFS first, server URL next, peer fallback last. Native share is an OS export, not paired-device transfer. | `exportToDevice`, `export.js` | +| Profile / playlist share and send | Metadata only. Sending a playlist does not transfer its media files. | Profile/playlist sync APIs | +| Verify & share | Explicit device upload to server intake; hash and media validation admit it into the server cache. | `P2PClient.contribute`, `p2p-intake.js` | +| Automatic rehydration | Server requests a holder to upload its full file to intake. This is an implicit media-byte server path. | `createRehydrator`, client `upload-request` handler | +| Admin uploads / original YouTube downloads | Intentional server ingestion of original media; separate from direct device transfer. | Upload and media-cache endpoints | +| VLC / external player URLs | HTTP server media URLs; these players cannot consume this application's DataChannel. | External player actions | + +The current file protocol has no resume: its receiver truncates partial files and +removes them on failure. Connection timeout is 20 seconds per holder. Device +signalling authenticates a device, but does not constrain its destination to a +verified profile or paired room. Profile names supplied at device registration +are not proof of profile membership. Existing server-verified holdings also +exclude files whose bytes the server has never admitted. + +## Implementation design + +Reuse ordered RTCDataChannel transfers with public STUN only. The WebSocket +server carries bounded, validated signalling and metadata, never file chunks. +Pair authorization must come from an authenticated remote/party room or a +verified profile session, not an arbitrary client-supplied profile name. + +A receiver explicitly accepts an invitation before saving. A cryptographically +random, expiring, one-use invitation binds sender, receiver and file identity; +subsequent signalling remains bound to that pair. Reject replay, mismatched +identities, binary WebSocket messages and unrecognized protocol fields. + +Send 64 KiB chunks with bufferedAmount backpressure. Persist a partial OPFS file +and resume at its exact committed byte offset. Rehash the retained prefix before +continuing and verify total size and SHA-256 before promoting the file into the +saved store; only then record metadata. A changed hash/size cannot reuse a partial. + +Use a 10-second connection deadline and an explicit, clearly labelled server +fallback offer. Direct mode must not silently rehydrate server media from a +device. Register “Direct device transfer (P2P)” in Settings, default on. + +Party playback should request a host's saved file when announced, then play the +receiver's OPFS copy. Remote controls and presenter lyrics retain their existing +behaviour; add explicit receive/play actions rather than automatically starting +media in a control-only screen. A manual send action targets authenticated peers. + +Ordinary faststart MP4 cannot safely be appended as arbitrary MediaSource chunks. +Progressive playback requires supported fragmented media and codec detection; +otherwise use the completed, verified file (including iOS). Do not claim that +raw MP4 chunks provide progressive playback. + +## Verification required before release + +Pure tests cover protocol parsing, chunk bounds, offsets, invitation replay and +pair authorization. A two-page localhost browser test must transfer real bytes +into OPFS through WebRTC and assert that server HTTP/WS logs contain only +signalling and metadata. Exercise interruption/resume and explicit fallback. +Review real iPhone foreground/background behaviour and restrictive NAT failure. + +## Implementation status + +This document records the pre-implementation audit and design. Transport, +authorization and UI changes described above are not yet implemented.