Type Alias: SourceResolver
type SourceResolver = (request: SourceResolutionRequest) => string | Promise<string>;
Defined in: packages/player/src/source-resolver.ts:58
Turns the logical URI in a playlist into the concrete URL mpv should open.
Parameters
| Parameter | Type | Description |
|---|---|---|
request | SourceResolutionRequest | The URI mpv is about to open. request.entryId is mpv's playlist entry id and is present only when the request came from the prefetch path — see SourceResolutionRequest. |
Returns
string | Promise<string>
The URL to open, synchronously or as a promise.
Remarks
Determinism is a requirement, not a nicety. While a queue is active the
same input must produce the same output. mpv opens each entry twice — once
speculatively on the prefetch path, once for real — and decides whether the
prefetched stream can be reused by comparing the two resulting URLs
byte-for-byte (open_demux_reentrant, mpv 0.41.0 player/loadfile.c:1223).
A resolver that mints a fresh nonce or a fresh signature per call therefore
silently defeats prefetching: mpv logs "Dropping finished prefetch of wrong
URL", joins the doomed opener thread on its core thread at the track
boundary, and opens cold — which is worse than never prefetching at all.
This library removes most of that hazard for you by caching the first answer
per URI (see resolverTtlMs) and replaying it for the second pass, so a
resolver only has to be deterministic for as long as its answer is cached.
Mint your signed URL once per track, not once per call, and you are fine.
The resolver runs on the JavaScript thread and it is allowed to be slow —
within a budget. Resolution happens ahead of time wherever possible: the
current and next entries are resolved as soon as the queue moves, which is
typically a whole track's worth of wall time before mpv needs the answer.
When mpv asks for a URI that is not cached yet, and it asks at play time,
mpv's core is held open until you answer or resolverTimeoutMs elapses. On
timeout the logical URI is used unchanged and mpv fails the load on its own
terms, which arrives as an ordinary typed error event.
URIs pass through untouched when no resolver is installed. The hooks are
registered for the life of the core (see Player.setSourceResolver for why),
but a disarmed handler reads nothing and rewrites nothing — it continues the
hook immediately, so what mpv opens is byte-for-byte the logical URI. That
includes local files and live streams, which usually need no resolution: a
resolver that has nothing to do for a URI should return it unchanged, and the
identity answer costs one map lookup.
Example
player.setSourceResolver(async ({ uri }) => {
if (!uri.startsWith('library://')) return uri // nothing to do
const { url } = await api.signPlaybackUrl(uri.slice('library://'.length))
return url
})