Skip to content

eufy-mega / core/contracts / 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).

Methods

live()

ts
live(opts?): Promise<LiveStreamHandle>;

Open a managed live stream.

Parameters

opts?

Record<string, unknown>

Returns

Promise<LiveStreamHandle>

Example

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

openReadable()?

ts
optional openReadable(opts?): Promise<Readable>;

Open a node:stream Readable of the live feed over a shared source consumer — raw Annex-B bytes (default) or objectMode LiveVideoFrames. In-process egress; the caller pipes it and owns its lifetime (destroying it releases the shared pull). Optional (unbound model has no client).

Parameters

opts?
audio?

boolean

objectMode?

boolean

Returns

Promise<Readable>


record()

ts
record(seconds, opts?): Promise<Buffer<ArrayBufferLike>>;

Record seconds of video → an mp4/h264 buffer.

Parameters

seconds

number

opts?
skipKeyframes?

number

timeoutMs?

number

Returns

Promise<Buffer<ArrayBufferLike>>


recordFragments()?

ts
optional recordFragments(opts?): AsyncIterable<MediaFragment>;

Continuously record the live feed as fragmented-MP4 (CMAF) — an async iterable of MediaFragment (init segment first, then a fragment per keyframe boundary), muxed dependency-free. break/return releases the shared pull. Optional.

Parameters

opts?
fragmentSeconds?

number

Returns

AsyncIterable<MediaFragment>


snapshot()

ts
snapshot(opts?): Promise<{
  file: string;
  jpeg: Buffer;
}>;

A still image for the camera. Tries the latest stored still first (cheap, no pull); when the device has no stored still it falls back to a live burst (one keyframe off the shared source, reusing a warm pull if one exists). file is the on-device path of the stored still, or "" when the JPEG was live-derived by the fallback. Rejects with a SnapshotUnavailableError (whose reason says whether the device was unreachable or simply had no image) when neither path yields a frame — the caller decides how to present that (e.g. an "offline" vs "unavailable" placeholder; the SDK never returns placeholder bytes).

Short-TTL cached + coalesced. A caller polling the still every few seconds returns the last still for cacheTtlMs (default DEFAULT_SNAPSHOT_CACHE_MS) instead of re-hitting the wire, and concurrent calls for the same device share ONE in-flight fetch — so a battery camera isn't woken on every poll. Only successes are cached; a failure is not. Pass cacheTtlMs: 0 to force a fresh fetch. Tune the TTL to the caller's refresh cadence (the SDK owns the mechanic, the caller owns the cadence — same split as the live power budget).

Parameters

opts?
cacheTtlMs?

number

timeoutMs?

number

Returns

Promise<{ file: string; jpeg: Buffer; }>


snapshotLive()

ts
snapshotLive(opts?): Promise<{
  height: number;
  jpeg: Buffer;
  width: number;
}>;

A fresh still decoded from a short live burst.

Parameters

opts?
collectMs?

number

skipKeyframes?

number

timeoutMs?

number

Returns

Promise<{ height: number; jpeg: Buffer; width: number; }>

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