@mega-yfue/eufy-sdk / MediaProvider
Interface: MediaProvider
The media / device-query boundary — the second transport, for operations that RETURN data (a still, a live stream, a recording, a P2P request/reply query). The client implements it (P2P media plumbing); capability modules call it without knowing the protocol. Bound to one device serial, so methods take none. p2pQuery is a GENERIC request/reply primitive (transport only). Optional because an unbound model has no live client (hence ?. at the call site).
Properties
views?
views?: readonly number[];Optical view identifiers supported by this bound media route.
Methods
downloadRecording()?
downloadRecording?(opts): Promise<RecordingDownload>;Download one recording the camera's station stored, identified by the recording name and cipher id of the event. One download runs at a time per station. Rejects with RecordingDownloadError. Optional, and absent on a camera whose station's recording download is unverified.
Parameters
opts
object & AbortableCall
Returns
Promise<RecordingDownload>
live()
live(opts?): Promise<LiveStreamConsumer>;Open a managed live stream.
Several cameras behind one station stream at the same time, each over its own connection to it. One connection serves one camera — a station answers the most recent start on a session, so two cameras sharing one take it from each other in turn — so a camera asked for while its station is already serving another gets a connection of its own. Measured on a base carrying two attached cameras, one at 3840x2160: both held full frame rate at once. Each handle receives only the frames the station tagged for ITS camera.
Parameters
opts?
SharedSourceHints & AbortableCall & MediaSourceSelection & Record<string, unknown>
Returns
Promise<LiveStreamConsumer>
Example
const stream = await cam.live?.();
stream?.on("video", (frame) => sink.write(frame.data)); // Annex-B
stream?.stop(); // detach this consumeropenReadable()?
openReadable?(opts?): Promise<Readable>;Open a video-only node:stream Readable over a shared source consumer — raw Annex-B bytes (default) or objectMode LiveVideoFrames. With objectMode and arrivalTime, it yields ReceivedVideoFrame envelopes whose original source-arrival time survives retained replay. Raw mode ignores arrivalTime. Audio is available separately through live or muxed through recordFragments; it is never interleaved into raw video. The caller owns the Readable's lifetime, and destroying it releases the shared pull.
Parameters
opts?
object & SharedSourceHints & AbortableCall & MediaSourceSelection
Returns
Promise<Readable>
record()
record(seconds, opts?): Promise<Buffer<ArrayBufferLike>>;Record seconds of video → an mp4/h264 buffer.
Opens its OWN pull rather than joining the shared source, so it costs a second stream on a camera that is already streaming. recordFragments is a shared consumer like every other egress.
Always settles: it resolves once the requested window has elapsed — with the run the camera actually delivered inside it, which a camera that goes quiet mid-clip makes shorter than asked — and rejects when the pull fails or ends before that window is up, or when no keyframe arrives to start the clip at.
Parameters
seconds
number
opts?
skipKeyframes?
number
timeoutMs?
number
Returns
Promise<Buffer<ArrayBufferLike>>
recordFragments()?
recordFragments?(opts?): FragmentRecordingHandle;Continuously record the live feed as fragmented-MP4 (CMAF). The caller-owned FragmentRecordingHandle yields an init segment then keyframe-bounded media fragments, emits battery-budget notices, and releases the shared pull on stop, break, or return.
SharedSourceHints.preBufferSeconds does double duty here: it configures the retained window when this call is the one that opens the pull, and it is the length this recording drains from a pull that was already open. The drain opens on the newest keyframe at or before the window starts, so it covers the request and exceeds it by however far back that keyframe sits.
Parameters
opts?
object & SharedSourceHints & AbortableCall
Returns
snapshotLive()
snapshotLive(opts?): Promise<{
height: number;
jpeg: Buffer;
retained?: true;
width: number;
}>;A fresh still decoded from a short live burst.
width/height describe the RETURNED IMAGE, read back out of it rather than taken from the stream's frame header: the header states the geometry at capture start, and a camera whose stream reconfigures mid-burst leaves it contradicting the bytes — which a caller sizing a buffer or caching by resolution cannot detect short of parsing the JPEG itself.
Rejects with LiveSnapshotUnavailableError, whose LiveSnapshotUnavailableError.retryable says whether another attempt could succeed.
Carries SharedSourceHints for the reason stated there: a still polled on an idle camera is routinely the call that OPENS the shared pull, so it decides the power budget and the retained window for every egress that joins later.
Parameters
opts?
object & SharedSourceHints & AbortableCall & MediaSourceSelection
Returns
Promise<{ height: number; jpeg: Buffer; retained?: true; width: number; }>
snapshotStored()?
snapshotStored?(): Promise<Buffer<ArrayBufferLike>>;Return the latest validated push thumbnail retained in memory. This passive operation performs no network, storage, P2P, live-media, or transcoding work at call time. It rejects with StoredSnapshotUnavailableError when no image is retained. Optional because cache ownership and capability binding belong to the client.
Returns
Promise<Buffer<ArrayBufferLike>>
talkback()?
talkback?(opts?): Promise<TalkbackHandle>;Open the camera's talkback path — audio travelling from the host TO the device, the opposite direction to everything else here. See TalkbackHandle for the accepted audio. Optional (an unbound model has no client), and absent on a device whose talkback wire is unverified.
Parameters
opts?
object & SharedSourceHints
Returns
Promise<TalkbackHandle>