# 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.