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()
live(opts?): Promise<LiveStreamHandle>;Open a managed live stream.
Parameters
opts?
Record<string, unknown>
Returns
Promise<LiveStreamHandle>
Example
const stream = await cam.live();
stream.on("video", (frame) => write(frame.data)); // Annex-B
stream.stop(); // detach this consumeropenReadable()?
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()
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()?
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()
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()
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; }>