Core: Time & State
A progress bar needs position, duration, and buffered range in one read; a status pill needs a typed enum, not a raw backend string. The kit provides both.
Time
time() reads the current position in seconds; time(seconds) seeks to a new one, there is no separate seek() method. Position is reported by two different events for two different reasons: ordinary playback ticks the time event (per frame) and the throttled progress event, while a deliberate jump fires the cancellable beforeSeek → seek → seeked cycle. It is the split the web platform draws between timeupdate and seeking/seeked, the tick versus the jump. buffered(), bufferedRanges(), and seekable() read the rest of the position picture straight from the backend, each returning a safe zero or empty range when no backend is registered yet. duration() returns the last value the backend reported through the duration event, or 0 before metadata has loaded.
timeData() bundles all of it into one TimeState snapshot: time (alias of position), position, duration, buffered, remaining, and percentage, computed together so the values are consistent at the moment of the call instead of drifting across four separate reads.
playbackRate() reads the current multiplier; playbackRate(rate) clamps it to [0.25, 2] and dispatches beforePlaybackRate before applying it, same cancellable shape as transport. It fires unconditionally, independent of setup({ mutationGuards }), since a speed change is cheap and every consumer expects it to always be interceptable. playbackRates() returns the fixed list of values a speed-selector UI should offer: [0.5, 0.75, 1, 1.25, 1.5, 2].
Coarse state
Four readers turn backend and platform detail into a typed enum:
bufferState():BufferState.IDLE/LOADING/SEEKING/STALLED, derived from the active backend.IDLEwhen no backend is registered.networkState():NetworkState.ONLINE/OFFLINE/SLOW, from the platform’s network monitor.ONLINEwhen none is configured,SLOWbelow a 1.5 Mbps downlink.streamState(): the backend’s raw state string ('idle','loading','playing', …). PreferbufferState()for typed access.visibilityState():VisibilityState.VISIBLE/HIDDEN, from the platform’s visibility monitor.VISIBLEwhen none is configured.
Every one of these has a safe default for the no-backend, no-monitor case, which is exactly the state a kit composition like MinimalPlayer is in until you attach real infrastructure.
/**
* Time and coarse-state reads. `timeData()` bundles position, duration,
* buffered, remaining, and percentage into one snapshot instead of four
* separate calls. The state readers (`bufferState`, `networkState`, and
* their siblings `streamState` / `visibilityState` / `qualityMode` /
* `audioTrackMode`) return typed enum values instead of raw backend strings,
* with safe defaults when no backend or platform monitor is registered.
*/
import { BufferState, NetworkState } from '@nomercy-entertainment/nomercy-player-core';
import { tourPlayer } from './tour-player';
const player = tourPlayer('time-state-demo');
player.setup({ logLevel: 'info' });
await player.ready();
console.log(player.timeData());
// { position: 0, duration: 0, buffered: 0, remaining: 0, percentage: 0 } — no media loaded yet
console.log(player.bufferState() === BufferState.IDLE); // true — no backend registered
console.log(player.networkState() === NetworkState.ONLINE); // true — no monitor configured, safe default
// playbackRate() dispatches beforePlaybackRate before applying the clamped rate.
player.on('beforePlaybackRate', (event) => {
event.data.rate = Math.min(event.data.rate, 1.5); // clamp to a house limit below the kit's own [0.25, 2]
});
await player.playbackRate(2);
console.log(player.playbackRate()); // 1.5 — the listener's mutation won
await player.dispose();
Next steps
- The Queue: the cursor-aware list every
next()/previous()/item()call reads from.