Skip to main content

Function: usePrefetchStatus()

function usePrefetchStatus(player: Player | undefined): PrefetchStatus;

Defined in: packages/player/src/hooks/usePrefetchStatus.ts:88

The player's prefetchStarted signal as renderable state: active from the moment mpv opens the next entry early, cleared at the boundary that consumes it.

const prefetch = usePrefetchStatus(player)
return prefetch.active
? <Badge label={`next ready · ${prefetch.uri}`} />
: null

Parameters

ParameterTypeDescription
playerPlayer | undefinedThe player, or undefined before it has been created.

Returns

PrefetchStatus

See PrefetchStatus. { active: false } while no prefetch is in flight — which, per the event's own two honest conditions (PlayerEventMap.prefetchStarted), is always when prefetchPlaylist is off or the linked libmpv lacks the prefetch hook. An idle status is not a failure signal.

Remarks

Why this is a hook and not player state. prefetchStarted is a discrete event; folding it into PlayerState would put a field on every snapshot that only a debug/status surface reads, and the snapshot would then need its own clearing rules. The event → state reduction is four subscriptions and one useState — exactly what a hook is for, and apps that never mount one pay nothing (the player walks an empty listener set).

When it clears — the honest set, from the existing event map only:

  • trackChanged — the boundary arrived. Either the prefetched entry became current (the gapless case) or a queue edit made mpv open something else cold (mpv logs Dropping finished prefetch of wrong URL. — see PlaylistAddOptions.position); in both cases nothing is opened-early anymore. This also covers stop(), which moves the cursor to -1.
  • error — the player gave up on the current entry; whatever happens next arrives through its own events.
  • queueEnded — the queue finished; there is no next entry to be warm.

Deliberately not on a timer and not on seekStarted: a seek within the current track does not invalidate the next entry's open demuxer.

No native surface is involved — this rides Player.on, so the subscription lifecycle (and its teardown on unmount and on player swap) is the whole implementation.