Skip to main content

Interface: ReducerContext

Defined in: packages/player/src/state.ts:402

Everything the reducer needs that is not carried by the event itself.

Remarks

A reducer over a real-time stream cannot be pure and call Date.now(). Time, the one-shot time-pos resync, the one-shot track-change reads and the current URI are therefore injected — which is exactly what makes the state machine reproducible from recorded fixtures.

Properties

now

readonly now: number;

Defined in: packages/player/src/state.ts:404

Date.now() at the moment the batch was received, in milliseconds.


timePos?

readonly optional timePos?: number;

Defined in: packages/player/src/state.ts:409

time-pos, read once per batch and only when the batch contains a position discontinuity. undefined when mpv reported it unavailable.


trackChange?

readonly optional trackChange?: TrackChangeReads;

Defined in: packages/player/src/state.ts:444

duration / seekable / media-title, read once per batch and only when the batch carries a playlist-pos change.

Remarks

This exists because mpv will not tell us these a second time. Two facts of mpv's property-observation contract (player/client.c, confirmed on-device 2026-08-11) combine against a "drop it and wait for the re-publication" strategy on a gapless transition:

  1. An observed property is delivered again only when its new value compares unequal to the one last sent (send_client_property_changes). Two consecutive tracks of the same length therefore never produce a second duration event at all.
  2. gen_property_change_event walks a client's observers in registration order, and OBSERVED_PROPERTIES registers duration before playlist-pos. On a gapless transition the new entry's duration thus arrives in the same batch, before the cursor change that would drop it.

So the re-publication the cursor change waits for has either already gone past or will never come, and duration/seekable/title stay undefined for the rest of the entry. Reordering the observation table fixes neither (fact 1 is independent of order). The Player instead reads the three values synchronously when it sees the cursor move and injects them here, exactly as it does for timePos.

They are "now" values — read when the batch reached JavaScript, not at the instant mpv generated the event — the same accepted approximation as timePos. When a value is genuinely not known yet (a network entry that is not demuxed), the key is absent and the field is dropped; mpv will send a change event once it becomes known, because none → value compares unequal.


uri?

readonly optional uri?: string;

Defined in: packages/player/src/state.ts:446

The source URI currently loaded, used to classify failures.