Skip to content

@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? ​

ts
views?: readonly number[];

Optical view identifiers supported by this bound media route.

Methods ​

downloadRecording()? ​

ts
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() ​

ts
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 ​

ts
const stream = await cam.live?.();
stream?.on("video", (frame) => sink.write(frame.data)); // Annex-B
stream?.stop(); // detach this consumer

openReadable()? ​

ts
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() ​

ts
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()? ​

ts
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 ​

FragmentRecordingHandle


snapshotLive() ​

ts
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()? ​

ts
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()? ​

ts
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>

Independent and unofficial. Not affiliated with, endorsed by, or sponsored by Anker Innovations, Anker eufy, or eufy. "Anker eufy", "eufy" and "Anker" are trademarks of their respective owners. Use responsibly — rapid or failed logins can trigger captcha or temporary cooldowns.