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:
- 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 seconddurationevent at all. gen_property_change_eventwalks a client's observers in registration order, andOBSERVED_PROPERTIESregistersdurationbeforeplaylist-pos. On a gapless transition the new entry'sdurationthus 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.