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
| Parameter | Type |
|---|---|
event | ChapterChangedEvent |
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
| Parameter | Type | Description |
|---|---|---|
error | PlayerError | The typed failure. |
info | PlayerErrorInfo | How 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
| Parameter | Type |
|---|---|
event | LogEvent |
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
| Parameter | Type |
|---|---|
metadata | Metadata |
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.titleis mpv'smedia-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 asStreamTitlechanges.- This event plus Player.getMetadataValue is the full tag map, for
apps that need a specific key —
icy-namefor the station,icy-brfor 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
| Parameter | Type |
|---|---|
event | PrefetchStartedEvent |
Returns
void
Remarks
Two conditions, both of them honest.
- 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.
- The linked libmpv must carry the prefetch hook. Stock libmpv runs no
hooks on its prefetch path and upstream documents that as permanent
(
options.rston--prefetch-playlist: URLs resolved by a hook "won't" work). The rn-media forks addon_prefetch_load: Androidv1.1.9-rnmedia.5+ and iOSv0.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, andprefetchis 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
| Parameter | Type |
|---|---|
event | QueueChangedEvent |
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 amove,shuffleorunshuffleand mpv accepted it. Emitted by those methods, because a reorder changes no observable property at all:playlist-countis identical andplaylist-posmay 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
| Parameter | Type |
|---|---|
event | RetryingEvent |
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
| Parameter | Type |
|---|---|
event | SeekCompletedEvent |
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
| Parameter | Type |
|---|---|
event | SeekStartedEvent |
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
| Parameter | Type |
|---|---|
event | TrackChangedEvent |
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
| Parameter | Type |
|---|---|
event | TrackEndedEvent |
Returns
void