Skip to main content

Interface: PlayerEventMap

Defined in: packages/player/src/player.ts:897

Discrete events, as opposed to the whole-state subscription.

trackEnded and error are deliberately distinct: a natural end of stream and a premature/failed one are different facts, and mapping mpv's end-file onto them lives in errors.ts.

Properties

chapterChanged

chapterChanged: (event: ChapterChangedEvent) => void;

Defined in: packages/player/src/player.ts:986

The current chapter changed — including moving off, or onto, an entry that has chapters at all.

Parameters

ParameterType
eventChapterChangedEvent

Returns

void

Remarks

Driven by mpv's observed chapter property, so it fires when the position crosses a chapter boundary during ordinary playback just as it does for Player.setChapter. Read the chapter's title with Player.getChapters; this event carries only the cursor, exactly as trackChanged does for the queue.


error

error: (error: PlayerError, info: PlayerErrorInfo) => void;

Defined in: packages/player/src/player.ts:915

Playback failed, finally. Also reflected in state.error with status: 'error'.

Parameters

ParameterTypeDescription
errorPlayerErrorThe typed failure.
infoPlayerErrorInfoHow many automatic re-attempts preceded it. A listener written as (error) => … stays valid; the second argument is additive.

Returns

void

Remarks

With PlayerOptions.retry enabled this fires only once the attempt budget is spent — an entry that failed and then played on the second try produces PlayerEventMap.retrying and no error at all. That is the point of the feature, and it is also the one thing to know before treating this event as "count of failures": it counts give-ups, not failures.


log

log: (event: LogEvent) => void;

Defined in: packages/player/src/player.ts:1093

An mpv log line, at or below the configured logLevel.

Parameters

ParameterType
eventLogEvent

Returns

void


metadataChanged

metadataChanged: (metadata: Metadata) => void;

Defined in: packages/player/src/player.ts:1058

The current entry's metadata changed — a new track's tags, or a live stream's now-playing update (ICY StreamTitle, which mpv surfaces as the icy-title tag and folds into media-title).

Fires at most once per native event batch, and only while at least one listener is registered: building the map costs one property read, so a player nobody is asking pays nothing.

Parameters

ParameterType
metadataMetadata

Returns

void

Remarks

This is the tag-store route; PlayerState.title is the now-playing route. The split is deliberate and it is not redundancy:

  • state.title is mpv's media-title — one coalesced string, carried in every snapshot, so it reaches the media session, the lock screen and the app's own UI through the ordinary state fan-out with no extra work and no extra read. On a radio stream it is the currently-playing song, and it updates on its own as StreamTitle changes.
  • This event plus Player.getMetadataValue is the full tag map, for apps that need a specific key — icy-name for the station, icy-br for the bitrate, album/date/musicbrainz_* for a library.

The map could not simply be a field of PlayerState, because building it is a synchronous read into mpv's core and most apps never look at it; and the title could not simply be an event, because a media session re-broadcasts state rather than events, so a now-playing line delivered only as an event would never reach the notification. Hence two routes, each paying only for what it is used for.

mpv invalidates metadata and media-title together on one MP_EVENT_METADATA_UPDATE (player/command.c), so the two are always describing the same instant — they are two views of one update, never two different truths.

Example

// The station, once per update; the song comes from state.title.
player.on('metadataChanged', (tags) => setStation(tags['icy-name']))

prefetchStarted

prefetchStarted: (event: PrefetchStartedEvent) => void;

Defined in: packages/player/src/player.ts:1091

mpv started opening the next queue entry ahead of time.

Fires once per prefetched entry, at the instant mpv releases its opener thread on it — which is seconds into the current track (mpv arms the prefetch on the first cache poll after the current file is fully read), not near the boundary. Use it to know that a transition is going to be gapless, to warm your own caches, or to log where a slow CDN is spending its time.

Parameters

ParameterType
eventPrefetchStartedEvent

Returns

void

Remarks

Two conditions, both of them honest.

  1. mpv must actually be prefetching, i.e. PlayerOptions.prefetchPlaylist is on (it is off by default — see there for why). Without it the event simply never occurs, because there is nothing to report.
  2. The linked libmpv must carry the prefetch hook. Stock libmpv runs no hooks on its prefetch path and upstream documents that as permanent (options.rst on --prefetch-playlist: URLs resolved by a hook "won't" work). The rn-media forks add on_prefetch_load: Android v1.1.9-rnmedia.5+ and iOS v0.7.2-rnmedia.4+, both mpv 0.41.0 (ARCHITECTURE §11). On any other build the hook is never raised — mpv accepts the registration and never fires it (client.h: "if the name is unknown, the hook event will simply be never raised") — so, again, the event never occurs. There is deliberately no error and no capability flag: an event that does not happen is not a failure, and prefetch is an optimisation whose absence is inaudible except in the handover gap.

entryId is mpv's playlist entry id and is present only on binaries that also expose prefetch-playlist-entry-id (the same fork releases). It is not a playlist index: ids survive playlist-move and playlist-remove.


queueChanged

queueChanged: (event: QueueChangedEvent) => void;

Defined in: packages/player/src/player.ts:951

The queue's contents changed. Read the new contents with PlaylistApi.entries.

Parameters

ParameterType
eventQueueChangedEvent

Returns

void

Remarks

What actually triggers it, honestly. There is no native observation of mpv's playlist array (see PlaylistApi.entries for why), so this event is derived from the two things this library genuinely knows:

  • reason: 'resized'playlist-count, which is observed, changed. It arrives on the ordinary event batch, so it is as late as any other state update and covers every add/remove/clear whatever issued it. Building a queue with Player.loadPlaylist may produce more than one, since the count climbs as entries are appended (the native batcher coalesces property changes, so it is usually far fewer than one per entry).
  • reason: 'reordered' — this library issued a move, shuffle or unshuffle and mpv accepted it. Emitted by those methods, because a reorder changes no observable property at all: playlist-count is identical and playlist-pos may or may not move. Nothing else can report it, and pretending otherwise would mean observing the whole playlist node.

The gap that leaves: a reorder issued through the raw Player.command escape hatch is invisible here. That is the price of not streaming the queue across the bridge, and it is written down rather than papered over.


queueEnded

queueEnded: () => void;

Defined in: packages/player/src/player.ts:972

The whole queue finished: the last entry ended naturally and nothing follows it.

Fires immediately after the trackEnded for that entry, and only when the queue is genuinely over — with loop: 'playlist' (or 'track') it never fires, because mpv is about to start something. It is the hook for autoplay, radio mode, "up next" recommendations and "playback finished" analytics, all of which trackEnded alone cannot express.

Returns

void

Remarks

Derived from the same snapshot pair every other event here is derived from: the entry ended (end-file reason eof) and the pre-end snapshot said PlayerState.hasNext was false. Nothing polls, and there is no new native signal — mpv has none to give.

A queue that stops because an entry failed does not produce this: that is the error event, and conflating "finished" with "gave up" is exactly the distinction this library is built around.


retrying

retrying: (event: RetryingEvent) => void;

Defined in: packages/player/src/player.ts:923

A failed entry is being re-attempted rather than skipped.

Fires once per attempt, immediately (there is no delay to wait out — see RetryOptions for why a JS-timer backoff would be a bug). Use it to show "reconnecting…" instead of an error banner.

Parameters

ParameterType
eventRetryingEvent

Returns

void


seekCompleted

seekCompleted: (event: SeekCompletedEvent) => void;

Defined in: packages/player/src/player.ts:1017

The position finished jumping and playback resumed — mpv's playbackRestart.

Carries the authoritative new position and the reason its seekStarted carried. See there for the pairing rules.

Parameters

ParameterType
eventSeekCompletedEvent

Returns

void


seekStarted

seekStarted: (event: SeekStartedEvent) => void;

Defined in: packages/player/src/player.ts:1009

The playback position is about to jump — a seek started, or the current entry changed.

Parameters

ParameterType
eventSeekStartedEvent

Returns

void

Remarks

This is the "position discontinuity" pair, and it is what makes accurate listening analytics possible without polling. Because position is projected locally and never streamed (ARCHITECTURE §7), an app that wants to know "how much of this track was actually heard" has to know when the clock jumped and from where. seekStarted.from is where it left, seekCompleted.position is where it landed, and everything between two of these was played in real time.

Every seekStarted is followed by exactly one seekCompleted unless mpv never restarts (the entry failed, or the player was destroyed mid-seek) — the pending reason is dropped on end-file, so a failed seek does not glue itself onto the next successful one. Deliberately not on start-file: an auto-advance announces itself when the cursor moves, and mpv's ordering of start-file against that cursor change is not guaranteed — clearing there could wipe a pending 'auto-advance' before the new entry's restart completes it.


trackChanged

trackChanged: (event: TrackChangedEvent) => void;

Defined in: packages/player/src/player.ts:974

The current playlist entry changed.

Parameters

ParameterType
eventTrackChangedEvent

Returns

void


trackEnded

trackEnded: (event: TrackEndedEvent) => void;

Defined in: packages/player/src/player.ts:899

A playlist entry reached its natural end (mpv end-file reason eof).

Parameters

ParameterType
eventTrackEndedEvent

Returns

void