4.8 KiB
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.