bun-mpv 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # bun-mpv
2
+
3
+ Control [mpv](https://mpv.io) from Bun. Talk to the player over its JSON IPC socket - no native bindings, no dependencies, no nonsense.
4
+
5
+ ## Why
6
+
7
+ mpv is the best media player out there. Sometimes you want to build something on top of it - a music server, a media kiosk, a custom UI, a streaming tool, whatever. This library gives you a clean JS API to do that, built specifically for Bun.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ bun add bun-mpv
13
+ ```
14
+
15
+ You need [mpv](https://mpv.io/installation/) installed on the system.
16
+
17
+ ## Quick Start
18
+
19
+ ```js
20
+ import { createMPV } from 'bun-mpv'
21
+
22
+ const player = await createMPV({
23
+ config: {
24
+ volume: 80,
25
+ fullscreen: true,
26
+ hardwareDecoding: 'auto',
27
+ }
28
+ })
29
+
30
+ await player.play('/path/to/video.mp4')
31
+
32
+ const pos = await player.position()
33
+ console.log(`Currently at ${pos}s`)
34
+
35
+ await player.observe('time-pos', (seconds) => {
36
+ console.log(seconds)
37
+ })
38
+ ```
39
+
40
+ ## How It Works
41
+
42
+ When you call `start()`, the library spawns an mpv process with `--input-ipc-server` pointing to a temporary unix socket (or named pipe on Windows). It then connects to that socket using `Bun.connect` and communicates with mpv through its [JSON IPC protocol](https://mpv.io/manual/master/#json-ipc).
43
+
44
+ Every mpv command and property is accessible. The library wraps the common ones with named methods so you don't have to memorize mpv's property names, but you can always drop down to the raw API:
45
+
46
+ ```js
47
+ // friendly
48
+ await player.volume(80)
49
+ await player.seek(30)
50
+ await player.togglePause()
51
+
52
+ // raw - same thing, full access to everything mpv supports
53
+ await player.set('volume', 80)
54
+ await player.command('seek', 30, 'relative')
55
+ await player.cycle('pause')
56
+ ```
57
+
58
+ ## Config
59
+
60
+ Instead of passing mpv CLI flags as strings, use a plain object:
61
+
62
+ ```js
63
+ const player = await createMPV({
64
+ config: {
65
+ volume: 70,
66
+ speed: 1.25,
67
+ videoOutput: 'gpu',
68
+ hardwareDecoding: 'auto',
69
+ ytdl: true,
70
+ loop: true,
71
+ fullscreen: false,
72
+ keepOpen: true,
73
+ subtitles: true,
74
+ subFontSize: 36,
75
+ },
76
+ // you can still pass raw flags if needed
77
+ args: ['--script=/path/to/something.lua'],
78
+ })
79
+ ```
80
+
81
+ ## Observing Properties
82
+
83
+ Watch any mpv property for real-time changes. Returns an unsubscribe function.
84
+
85
+ ```js
86
+ const unsub = await player.observe('volume', (vol) => {
87
+ console.log(`Volume: ${vol}`)
88
+ })
89
+
90
+ // or observe many at once
91
+ const unsubAll = await player.observeMany({
92
+ 'pause': (paused) => console.log(paused ? 'paused' : 'playing'),
93
+ 'time-pos': (pos) => updateUI(pos),
94
+ })
95
+
96
+ // later
97
+ unsub()
98
+ unsubAll()
99
+ ```
100
+
101
+ ## Events
102
+
103
+ Standard EventEmitter. mpv events are forwarded directly.
104
+
105
+ ```js
106
+ player.on('start', () => {})
107
+ player.on('exit', (code) => {})
108
+ player.on('file-loaded', () => {})
109
+ player.on('end-file', (ev) => {})
110
+ player.on('error', (err) => {})
111
+ ```
112
+
113
+ ## Cleanup
114
+
115
+ The player cleans up after itself on process exit. You can also manage it manually or use `await using` for automatic disposal:
116
+
117
+ ```js
118
+ // explicit
119
+ await player.quit() // graceful
120
+ await player.destroy() // immediate + permanent
121
+
122
+ // automatic (Explicit Resource Management)
123
+ {
124
+ await using player = await createMPV()
125
+ await player.play('song.mp3')
126
+ await Bun.sleep(10000)
127
+ } // destroyed automatically
128
+ ```
129
+
130
+ ## Auto-Restart
131
+
132
+ If mpv crashes, the library restarts it and re-subscribes all your observers. Disable with `autoRestart: false`.
133
+
134
+ ## Requirements
135
+
136
+ - [Bun](https://bun.sh)
137
+ - [mpv](https://mpv.io/installation/) in your PATH (or pass a custom path with `binary`)
138
+
139
+ ## License
140
+ Apache-2.0
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import{existsSync as G}from"fs";import{unlink as N}from"fs/promises";import{EventEmitter as O}from"events";var K=process.platform==="win32",T={volume:(H)=>`--volume=${H}`,speed:(H)=>`--speed=${H}`,videoOutput:(H)=>`--vo=${H}`,audioOutput:(H)=>`--ao=${H}`,hardwareDecoding:(H)=>`--hwdec=${H}`,fullscreen:(H)=>H?"--fullscreen":null,mute:(H)=>H?"--mute=yes":null,keepOpen:(H)=>H?"--keep-open=yes":null,ytdl:(H)=>H?"--ytdl=yes":null,ytdlFormat:(H)=>`--ytdl-format=${H}`,subtitles:(H)=>H===!1?"--no-sub":null,osd:(H)=>H===!1?"--osd-level=0":null,osdDuration:(H)=>`--osd-duration=${H}`,title:(H)=>`--title=${H}`,geometry:(H)=>`--geometry=${H}`,autofit:(H)=>`--autofit=${H}`,autofitLarger:(H)=>`--autofit-larger=${H}`,autofitSmaller:(H)=>`--autofit-smaller=${H}`,screenName:(H)=>`--screen-name=${H}`,borderless:(H)=>H?"--no-border":null,ontop:(H)=>H?"--ontop":null,cursor:(H)=>H===!1?"--no-osc":null,savePosOnQuit:(H)=>H?"--save-position-on-quit":null,profile:(H)=>`--profile=${H}`,loop:(H)=>{if(H===!0)return"--loop=inf";if(typeof H==="number")return`--loop=${H}`;return null},loopPlaylist:(H)=>{if(H===!0)return"--loop-playlist=inf";if(typeof H==="number")return`--loop-playlist=${H}`;return null},audioDevice:(H)=>`--audio-device=${H}`,audioChannels:(H)=>`--audio-channels=${H}`,videoSync:(H)=>`--video-sync=${H}`,interpolation:(H)=>H?"--interpolation":null,deinterlace:(H)=>H?"--deinterlace=yes":null,gpu:(H)=>`--gpu-context=${H}`,gpuApi:(H)=>`--gpu-api=${H}`,demuxerMaxBytes:(H)=>`--demuxer-max-bytes=${H}`,demuxerMaxBackBytes:(H)=>`--demuxer-max-back-bytes=${H}`,cacheSeconds:(H)=>`--cache-secs=${H}`,userAgent:(H)=>`--user-agent=${H}`,referrer:(H)=>`--referrer=${H}`,subFont:(H)=>`--sub-font=${H}`,subFontSize:(H)=>`--sub-font-size=${H}`,subColor:(H)=>`--sub-color=${H}`,subBorderColor:(H)=>`--sub-border-color=${H}`,subBorderSize:(H)=>`--sub-border-size=${H}`,subPos:(H)=>`--sub-pos=${H}`,screenshot:(H)=>`--screenshot-format=${H}`,screenshotDir:(H)=>`--screenshot-directory=${H}`,screenshotTemplate:(H)=>`--screenshot-template=${H}`};function _(H){let J=[];for(let[X,Z]of Object.entries(H)){let Y=T[X];if(!Y)continue;let $=Y(Z);if($)J.push($)}return J}var E=["--audio-fallback-to-null=yes","--no-config","--idle","--msg-level=all=error"];function I(){return(K?"\\\\.\\pipe\\mpvsocket":"/tmp/mpvsocket")+crypto.randomUUID()}function V(H){if(H){if(G(H))return H;throw Error(`MPV binary not found: ${H}`)}if(K)return G("mpv.exe")?"mpv.exe":"mpv";return G("./mpv")?"./mpv":"mpv"}async function F(H,J){if(!H)return;try{let X=H.getReader(),Z=new TextDecoder;while(!0){let{done:Y,value:$}=await X.read();if(Y)break;J(Z.decode($,{stream:!0}))}}catch{}}function C(H){let J=0;for(let X of H)J+=X.length;return J}class Q extends O{#Y=null;#$=null;#B=null;#N=null;#L=[];#O={};#T={};#C=0;#M=0;#z=new Map;#Z=new Map;#Q="";#X="closed";#U=!0;#_=5000;#D=null;#G=null;#K=null;#W=!1;constructor(H={}){super();this.#N=V(H.binary),this.#O=H.config||{},this.#L=H.args||[],this.#T=H.spawnOpts||{},this.#_=H.connectTimeout??5000,this.#U=H.autoRestart??!0,this.#D=H.stdout||null,this.#G=H.stderr||null}get state(){return this.#X}get pid(){return this.#Y?.pid??null}get running(){return this.#X==="running"}async start(){if(this.#X==="running"||this.#X==="starting")return this;if(this.#X==="destroyed")throw Error("MPV instance is destroyed — create a new one");this.#X="starting",this.#B=I(),this.#K=new AbortController,this.#W=!1;let H=_(this.#O),X=[`--input-ipc-server=${this.#B}`];for(let Y of H)if(!this.#L.some(($)=>$.startsWith(Y.split("=")[0])))X.push(Y);let Z=[...this.#L,...X];for(let Y of E)if(!Z.some(($)=>$.startsWith(Y.split("=")[0])))X.push(Y);X.push(...this.#L);try{this.#Y=Bun.spawn([this.#N,...X],{...this.#T,stdout:"pipe",stderr:"pipe",stdin:"ignore",signal:this.#K.signal}),F(this.#Y.stdout,($)=>{if(this.#D)this.#D($);this.emit("stdout",$)});let Y=[];return F(this.#Y.stderr,($)=>{Y.push($);while(C(Y)>1e4)Y.shift();if(this.#G)this.#G($);this.emit("stderr",$)}),await new Promise(($,B)=>{let z=!1;this.#Y.exited.then((L)=>{if(!z){z=!0;let D=Y.join("");B(Object.assign(Error(D||`mpv exited with code ${L}`),{code:L,signal:this.#Y?.signalCode}))}}),this.#y().then(()=>{if(!z)z=!0,$()},(L)=>{if(!z)z=!0,B(L)})}),this.#h(),this.#A(),this.#X="running",this.emit("start"),this.#Y.exited.then(($)=>{if(this.#W)return;this.#W=!0;let B=this.#X==="running";if(this.#X!=="destroyed")this.#X="closed";if(this.#F(),this.emit("exit",$),this.#U&&B&&this.#X!=="destroyed")this.start().catch((z)=>this.emit("error",z))}),this}catch(Y){throw this.#X="errored",this.#F(),Y}}async quit(H=0){if(!this.running)return;try{await this.#H("quit",H)}catch{}await this.#V(3000)}async destroy(){this.#U=!1,this.#X="destroyed",this.#K?.abort(),await this.#I(),this.#F(),this.#P("MPV destroyed"),this.removeAllListeners()}async load(H,{mode:J="replace",index:X,options:Z}={}){this.#J();let Y=["loadfile",H,J];if(X!==void 0)Y.push(X);else if(Z)Y.push(-1);if(Z)Y.push(Object.entries(Z).map(([$,B])=>`${$}=${B}`).join(","));return this.#H(...Y)}async loadList(H,J="replace"){return this.#J(),this.#H("loadlist",H,J)}async play(H,J){if(H)return this.load(H,J);return this.set("pause",!1)}async pause(){return this.set("pause",!0)}async resume(){return this.set("pause",!1)}async togglePause(){return this.cycle("pause")}async stop(H=!1){return this.#J(),this.#H("stop",H?"keep-playlist":void 0)}async seek(H,J="relative"){return this.#J(),this.#H("seek",H,J)}async seekTo(H){return this.seek(H,"absolute")}async seekPercent(H){return this.seek(H,"absolute-percent")}async revertSeek(H){return this.#J(),this.#H("revert-seek",H)}async frameStep(H=1,J){return this.#J(),this.#H("frame-step",H,J)}async frameBackStep(){return this.#J(),this.#H("frame-back-step")}async subSeek(H){return this.#J(),this.#H("sub-seek",H)}async volume(H){if(H!==void 0)return this.set("volume",H);return this.get("volume")}async adjustVolume(H){return this.add("volume",H)}async mute(H){if(H!==void 0)return this.set("mute",!!H);return this.get("mute")}async toggleMute(){return this.cycle("mute")}async audioTrack(H){if(H!==void 0)return this.set("aid",H);return this.get("aid")}async nextAudioTrack(){return this.cycle("aid")}async audioAdd(H,J="select"){return this.#J(),this.#H("audio-add",H,J)}async audioRemove(H){return this.#J(),this.#H("audio-remove",H)}async fullscreen(H){if(H!==void 0)return this.set("fullscreen",!!H);return this.get("fullscreen")}async toggleFullscreen(){return this.cycle("fullscreen")}async videoTrack(H){if(H!==void 0)return this.set("vid",H);return this.get("vid")}async videoAdd(H,J="select"){return this.#J(),this.#H("video-add",H,J)}async vf(H,J){return this.#J(),this.#H("vf",H,J)}async af(H,J){return this.#J(),this.#H("af",H,J)}async subTrack(H){if(H!==void 0)return this.set("sid",H);return this.get("sid")}async nextSubTrack(){return this.cycle("sid")}async subAdd(H,J="select",X,Z){this.#J();let Y=["sub-add",H,J];if(X!==void 0)Y.push(X);if(Z!==void 0)Y.push(Z);return this.#H(...Y)}async subRemove(H){return this.#J(),this.#H("sub-remove",H)}async subReload(H){return this.#J(),this.#H("sub-reload",H)}async subDelay(H){if(H!==void 0)return this.set("sub-delay",H);return this.get("sub-delay")}async subVisibility(H){if(H!==void 0)return this.set("sub-visibility",!!H);return this.get("sub-visibility")}async speed(H){if(H!==void 0)return this.set("speed",H);return this.get("speed")}async next(H=!1){return this.#J(),this.#H("playlist-next",H?"force":"weak")}async prev(H=!1){return this.#J(),this.#H("playlist-prev",H?"force":"weak")}async playIndex(H){return this.#J(),this.#H("playlist-play-index",H)}async append(H,J){return this.load(H,{mode:"append-play",options:J})}async insertNext(H,J){return this.load(H,{mode:"insert-next",options:J})}async playlistClear(){return this.#J(),this.#H("playlist-clear")}async playlistRemove(H="current"){return this.#J(),this.#H("playlist-remove",H)}async playlistMove(H,J){return this.#J(),this.#H("playlist-move",H,J)}async playlistShuffle(){return this.#J(),this.#H("playlist-shuffle")}async playlistUnshuffle(){return this.#J(),this.#H("playlist-unshuffle")}async playlist(){return this.get("playlist")}async playlistCount(){return this.get("playlist-count")}async playlistPos(H){if(H!==void 0)return this.set("playlist-pos",H);return this.get("playlist-pos")}async loopA(H){if(H!==void 0)return this.set("ab-loop-a",H);return this.get("ab-loop-a")}async loopB(H){if(H!==void 0)return this.set("ab-loop-b",H);return this.get("ab-loop-b")}async loopClear(){await this.set("ab-loop-a","no"),await this.set("ab-loop-b","no")}async loop(H){if(H!==void 0){if(H===!0)return this.set("loop-file","inf");if(H===!1)return this.set("loop-file","no");return this.set("loop-file",H)}return this.get("loop-file")}async loopPlaylist(H){if(H!==void 0){if(H===!0)return this.set("loop-playlist","inf");if(H===!1)return this.set("loop-playlist","no");return this.set("loop-playlist",H)}return this.get("loop-playlist")}async position(){return this.get("time-pos")}async duration(){return this.get("duration")}async remaining(){return this.get("time-remaining")}async progress(){return this.get("percent-pos")}async title(){return this.get("media-title")}async filename(){return this.get("filename")}async path(){return this.get("path")}async metadata(){return this.get("metadata")}async tracks(){return this.get("track-list")}async chapters(){return this.get("chapter-list")}async paused(){return this.get("pause")}async idle(){return this.get("idle-active")}async width(){return this.get("width")}async height(){return this.get("height")}async fps(){return this.get("estimated-vf-fps")}async audioBitrate(){return this.get("audio-bitrate")}async audioCodec(){return this.get("audio-codec-name")}async videoCodec(){return this.get("video-codec-name")}async fileSize(){return this.get("file-size")}async chapter(H){if(H!==void 0)return this.set("chapter",H);return this.get("chapter")}async status(){this.#J();let H=["pause","time-pos","duration","percent-pos","volume","mute","speed","media-title","filename","playlist-pos","playlist-count","chapter","idle-active","loop-file","loop-playlist"],J=await Promise.allSettled(H.map((Z)=>this.get(Z))),X={};for(let Z=0;Z<H.length;Z++){let Y=H[Z].replace(/-./g,($)=>$[1].toUpperCase());X[Y]=J[Z].status==="fulfilled"?J[Z].value:null}return X}async screenshot(H="subtitles"){return this.#J(),this.#H("screenshot",H)}async screenshotToFile(H,J="subtitles"){return this.#J(),this.#H("screenshot-to-file",H,J)}async showText(H,J=1000,X=1){return this.#J(),this.#H("show-text",H,J,X)}async showProgress(){return this.#J(),this.#H("show-progress")}async applyProfile(H,J="apply"){return this.#J(),this.#H("apply-profile",H,J)}async get(H){return this.#J(),this.#H("get_property",H)}async set(H,J){return this.#J(),this.#H("set_property",H,J)}async add(H,J=1){return this.#J(),this.#H("add",H,J)}async cycle(H,J){return this.#J(),this.#H("cycle",H,J)}async multiply(H,J){return this.#J(),this.#H("multiply",H,J)}async toggle(H){return this.cycle(H)}async command(...H){return this.#J(),this.#H(...H)}async observe(H,J){if(this.#J(),typeof J!=="function")throw TypeError("Observer callback must be a function");if(this.#Z.has(H))return this.#Z.get(H).fns.add(J),()=>this.unobserve(H,J);let X=++this.#M,Z={id:X,fns:new Set([J])};this.#Z.set(H,Z);try{await this.#H("observe_property",X,H)}catch(Y){throw this.#Z.delete(H),Y}return()=>this.unobserve(H,J)}async observeMany(H){let J=await Promise.all(Object.entries(H).map(([X,Z])=>this.observe(X,Z)));return()=>J.forEach((X)=>X())}async unobserve(H,J){let X=this.#Z.get(H);if(!X)return;if(J){if(X.fns.delete(J),X.fns.size>0)return}this.#Z.delete(H);try{await this.#H("unobserve_property",X.id)}catch{}}waitForEvent(H,J=30000){return new Promise((X,Z)=>{let Y=setTimeout(()=>{this.off(H,$),Z(Error(`Timed out waiting for event: ${H}`))},J),$=(B)=>{clearTimeout(Y),X(B)};this.once(H,$)})}async waitForProperty(H,J,X=30000){try{let Z=await this.get(H);if(Z===J)return Z}catch{}return new Promise((Z,Y)=>{let $=setTimeout(()=>{B(),Y(Error(`Timed out waiting for ${H} = ${J}`))},X),B;this.observe(H,(z)=>{if(z===J)clearTimeout($),B?.(),Z(z)}).then((z)=>{B=z})})}async[Symbol.asyncDispose](){await this.destroy()}async#y(){let H=Bun.nanoseconds()/1e6,J=this.#_;return new Promise((X,Z)=>{let Y=!1,$=()=>{if(Y)return;if(Bun.nanoseconds()/1e6-H>J)return Y=!0,Z(Error(`Timed out connecting to mpv socket (${J}ms)`));Bun.connect({unix:this.#B,socket:{open:(z)=>{if(Y){z.end();return}Y=!0,this.#$=z,z.unref(),X()},data:(z,L)=>{let D=typeof L==="string"?L:new TextDecoder().decode(L);this.#j(D)},close:()=>{if(!Y){setTimeout($,20);return}},error:(z,L)=>{if(!Y){setTimeout($,20);return}this.emit("error",L)},end:()=>{if(this.#X==="running")this.#I()}}}).catch(()=>{if(!Y)setTimeout($,20)})};$()})}#H(...H){let J=Error();return Error.captureStackTrace(J,this.#H),new Promise((X,Z)=>{let Y=++this.#C,$={id:Y,resolve:X,reject:Z,args:H,message:JSON.stringify({request_id:Y,command:H.filter((B)=>B!==void 0)})+`
2
+ `};if(this.#$&&this.#$.readyState===1)this.#E($);else $._queued=!0,this.#z.set(Y,$)}).catch((X)=>{throw J.message=X.message||String(X),J})}#E(H){if(this.#$)this.#$.write(H.message),H._queued=!1,this.#z.set(H.id,H)}#A(){for(let[H,J]of this.#z)if(J._queued)this.#E(J)}#j(H){this.#Q+=H;let J=this.#Q.split(`
3
+ `);this.#Q=J.pop();for(let X of J){let Z=X.trim();if(!Z)continue;try{this.#w(JSON.parse(Z))}catch{this.emit("error",Error(`Failed to parse mpv response: ${Z}`))}}}#w(H){if(H.event){if(H.event==="property-change"){let X=this.#Z.get(H.name);if(X)for(let Z of X.fns)try{Z(H.data,H.name)}catch(Y){this.emit("error",Y)}}this.emit(H.event,H);return}let J=this.#z.get(H.request_id);if(!J){if(H.error!=="success")this.emit("error",Object.assign(Error(H.error),H));return}if(this.#z.delete(H.request_id),H.error==="success")J.resolve(H.data);else J.reject(Error(`[${J.args.join(", ")}] failed: ${H.error}`))}#h(){for(let[H,{id:J}]of this.#Z)this.#H("observe_property",J,H).catch(()=>{})}#J(){if(this.#X!=="running")throw Error(`MPV is not running (state: ${this.#X}). Call .start() first.`)}#F(){try{this.#$?.end?.()}catch{}if(this.#$=null,this.#Q="",this.#B&&!K)N(this.#B).catch(()=>{})}#P(H){for(let[J,X]of this.#z)X.reject(Error(H));this.#z.clear()}async#I(){if(!this.#Y||this.#Y.killed)return;this.#Y.kill(),await this.#V(2000)}async#V(H){if(!this.#Y)return;try{await Promise.race([this.#Y.exited,new Promise((J,X)=>setTimeout(()=>X(),H))])}catch{try{this.#Y?.kill("SIGKILL")}catch{}}}}async function M(H={}){let J=new Q(H);return await J.start(),J}var U=new Set,h=Q.prototype.start,P=Q.prototype.destroy,y=Q.prototype.emit;Q.prototype.emit=function(H,...J){if(H==="start")U.add(this);if(H==="exit")U.delete(this);return y.call(this,H,...J)};function W(){for(let H of U)try{H.destroy()}catch{}U.clear()}process.on("exit",W);process.on("SIGINT",()=>{W(),process.exit(130)});process.on("SIGTERM",()=>{W(),process.exit(143)});var q=M;export{q as default,M as createMPV,Q as MPV};
4
+
5
+ //# debugId=2762E2DBD2E1CE0264756E2164756E21
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["..\\src\\index.js"],
4
+ "sourcesContent": [
5
+ "import { existsSync } from 'fs'\r\nimport { unlink } from 'fs/promises'\r\nimport { EventEmitter } from 'events'\r\n\r\nconst IS_WIN = process.platform === 'win32'\r\n\r\nconst CONFIG_MAP = {\r\n volume: v => `--volume=${v}`,\r\n speed: v => `--speed=${v}`,\r\n videoOutput: v => `--vo=${v}`,\r\n audioOutput: v => `--ao=${v}`,\r\n hardwareDecoding: v => `--hwdec=${v}`,\r\n fullscreen: v => v ? '--fullscreen' : null,\r\n mute: v => v ? '--mute=yes' : null,\r\n keepOpen: v => v ? '--keep-open=yes' : null,\r\n ytdl: v => v ? '--ytdl=yes' : null,\r\n ytdlFormat: v => `--ytdl-format=${v}`,\r\n subtitles: v => v === false ? '--no-sub' : null,\r\n osd: v => v === false ? '--osd-level=0' : null,\r\n osdDuration: v => `--osd-duration=${v}`,\r\n title: v => `--title=${v}`,\r\n geometry: v => `--geometry=${v}`,\r\n autofit: v => `--autofit=${v}`,\r\n autofitLarger: v => `--autofit-larger=${v}`,\r\n autofitSmaller: v => `--autofit-smaller=${v}`,\r\n screenName: v => `--screen-name=${v}`,\r\n borderless: v => v ? '--no-border' : null,\r\n ontop: v => v ? '--ontop' : null,\r\n cursor: v => v === false ? '--no-osc' : null,\r\n savePosOnQuit: v => v ? '--save-position-on-quit' : null,\r\n profile: v => `--profile=${v}`,\r\n loop: v => {\r\n if (v === true) return '--loop=inf'\r\n if (typeof v === 'number') return `--loop=${v}`\r\n return null\r\n },\r\n loopPlaylist: v => {\r\n if (v === true) return '--loop-playlist=inf'\r\n if (typeof v === 'number') return `--loop-playlist=${v}`\r\n return null\r\n },\r\n audioDevice: v => `--audio-device=${v}`,\r\n audioChannels: v => `--audio-channels=${v}`,\r\n videoSync: v => `--video-sync=${v}`,\r\n interpolation: v => v ? '--interpolation' : null,\r\n deinterlace: v => v ? '--deinterlace=yes' : null,\r\n gpu: v => `--gpu-context=${v}`,\r\n gpuApi: v => `--gpu-api=${v}`,\r\n demuxerMaxBytes: v => `--demuxer-max-bytes=${v}`,\r\n demuxerMaxBackBytes: v => `--demuxer-max-back-bytes=${v}`,\r\n cacheSeconds: v => `--cache-secs=${v}`,\r\n userAgent: v => `--user-agent=${v}`,\r\n referrer: v => `--referrer=${v}`,\r\n subFont: v => `--sub-font=${v}`,\r\n subFontSize: v => `--sub-font-size=${v}`,\r\n subColor: v => `--sub-color=${v}`,\r\n subBorderColor: v => `--sub-border-color=${v}`,\r\n subBorderSize: v => `--sub-border-size=${v}`,\r\n subPos: v => `--sub-pos=${v}`,\r\n screenshot: v => `--screenshot-format=${v}`,\r\n screenshotDir: v => `--screenshot-directory=${v}`,\r\n screenshotTemplate: v => `--screenshot-template=${v}`,\r\n}\r\n\r\nfunction buildConfigArgs(config) {\r\n const args = []\r\n for (const [key, value] of Object.entries(config)) {\r\n const mapper = CONFIG_MAP[key]\r\n if (!mapper) continue\r\n const flag = mapper(value)\r\n if (flag) args.push(flag)\r\n }\r\n return args\r\n}\r\n\r\nconst DEFAULT_ARGS = [\r\n '--audio-fallback-to-null=yes',\r\n '--no-config',\r\n '--idle',\r\n '--msg-level=all=error',\r\n]\r\n\r\nfunction makeSocketPath() {\r\n const base = IS_WIN ? '\\\\\\\\.\\\\pipe\\\\mpvsocket' : '/tmp/mpvsocket'\r\n return base + crypto.randomUUID()\r\n}\r\n\r\nfunction resolveBinary(custom) {\r\n if (custom) {\r\n if (existsSync(custom)) return custom\r\n throw new Error(`MPV binary not found: ${custom}`)\r\n }\r\n if (IS_WIN) return existsSync('mpv.exe') ? 'mpv.exe' : 'mpv'\r\n return existsSync('./mpv') ? './mpv' : 'mpv'\r\n}\r\n\r\nasync function consumeStream(stream, fn) {\r\n if (!stream) return\r\n try {\r\n const reader = stream.getReader()\r\n const decoder = new TextDecoder()\r\n while (true) {\r\n const { done, value } = await reader.read()\r\n if (done) break\r\n fn(decoder.decode(value, { stream: true }))\r\n }\r\n } catch {}\r\n}\r\n\r\nfunction totalSize(arr) {\r\n let n = 0\r\n for (const s of arr) n += s.length\r\n return n\r\n}\r\n\r\nexport class MPV extends EventEmitter {\r\n\r\n // Private state\r\n #proc = null\r\n #socket = null\r\n #socketPath = null\r\n #binary = null\r\n #args = []\r\n #config = {}\r\n #spawnOpts = {}\r\n #requestId = 0\r\n #observeId = 0\r\n #requests = new Map()\r\n #observers = new Map()\r\n #buffer = ''\r\n #state = 'closed' // closed | starting | running | errored | destroyed\r\n #autoRestart = true\r\n #connectTimeout = 5000\r\n #onStdout = null\r\n #onStderr = null\r\n #abortController = null\r\n #exitSettled = false\r\n\r\n /**\r\n * @param {object} options\r\n * @param {string} [options.binary] - Path to mpv binary\r\n * @param {object} [options.config] - Friendly config object (see CONFIG_MAP)\r\n * @param {string[]} [options.args] - Additional raw mpv CLI args\r\n * @param {object} [options.spawnOpts] - Extra options passed to Bun.spawn\r\n * @param {number} [options.connectTimeout] - Socket connect timeout in ms (default 5000)\r\n * @param {boolean} [options.autoRestart] - Auto-restart on crash (default true)\r\n * @param {Function} [options.stdout] - Stdout handler\r\n * @param {Function} [options.stderr] - Stderr handler\r\n */\r\n constructor(options = {}) {\r\n super()\r\n this.#binary = resolveBinary(options.binary)\r\n this.#config = options.config || {}\r\n this.#args = options.args || []\r\n this.#spawnOpts = options.spawnOpts || {}\r\n this.#connectTimeout = options.connectTimeout ?? 5000\r\n this.#autoRestart = options.autoRestart ?? true\r\n this.#onStdout = options.stdout || null\r\n this.#onStderr = options.stderr || null\r\n }\r\n\r\n\r\n /* 'closed' | 'starting' | 'running' | 'errored' | 'destroyed' */\r\n get state() { return this.#state }\r\n get pid() { return this.#proc?.pid ?? null }\r\n get running() { return this.#state === 'running' }\r\n\r\n async start() {\r\n if (this.#state === 'running' || this.#state === 'starting') return this\r\n if (this.#state === 'destroyed')\r\n throw new Error('MPV instance is destroyed — create a new one')\r\n\r\n this.#state = 'starting'\r\n this.#socketPath = makeSocketPath()\r\n this.#abortController = new AbortController()\r\n this.#exitSettled = false\r\n\r\n const configArgs = buildConfigArgs(this.#config)\r\n const socketArg = `--input-ipc-server=${this.#socketPath}`\r\n const allArgs = [socketArg]\r\n\r\n for (const a of configArgs) {\r\n if (!this.#args.some(x => x.startsWith(a.split('=')[0])))\r\n allArgs.push(a)\r\n }\r\n\r\n const combined = [...this.#args, ...allArgs]\r\n for (const a of DEFAULT_ARGS) {\r\n if (!combined.some(x => x.startsWith(a.split('=')[0])))\r\n allArgs.push(a)\r\n }\r\n\r\n allArgs.push(...this.#args)\r\n\r\n try {\r\n this.#proc = Bun.spawn([this.#binary, ...allArgs], {\r\n ...this.#spawnOpts,\r\n stdout: 'pipe',\r\n stderr: 'pipe',\r\n stdin: 'ignore',\r\n signal: this.#abortController.signal,\r\n })\r\n\r\n // Consume stdout/stderr\r\n consumeStream(this.#proc.stdout, text => {\r\n if (this.#onStdout) this.#onStdout(text)\r\n this.emit('stdout', text)\r\n })\r\n\r\n const stderrChunks = []\r\n consumeStream(this.#proc.stderr, text => {\r\n stderrChunks.push(text)\r\n while (totalSize(stderrChunks) > 10000) stderrChunks.shift()\r\n if (this.#onStderr) this.#onStderr(text)\r\n this.emit('stderr', text)\r\n })\r\n\r\n // Race: connect to IPC socket vs process dying\r\n await new Promise((resolve, reject) => {\r\n let settled = false\r\n\r\n this.#proc.exited.then(code => {\r\n if (!settled) {\r\n settled = true\r\n const stderr = stderrChunks.join('')\r\n reject(Object.assign(\r\n new Error(stderr || `mpv exited with code ${code}`),\r\n { code, signal: this.#proc?.signalCode }\r\n ))\r\n }\r\n })\r\n\r\n this.#connectSocket().then(\r\n () => { if (!settled) { settled = true; resolve() } },\r\n e => { if (!settled) { settled = true; reject(e) } },\r\n )\r\n })\r\n\r\n // Re-register observers\r\n this.#resubscribeObservers()\r\n\r\n // Flush queued commands\r\n this.#flushQueue()\r\n\r\n this.#state = 'running'\r\n this.emit('start')\r\n\r\n // Watch for exit (only register once for this start cycle)\r\n this.#proc.exited.then(code => {\r\n if (this.#exitSettled) return\r\n this.#exitSettled = true\r\n\r\n const wasRunning = this.#state === 'running'\r\n if (this.#state !== 'destroyed') this.#state = 'closed'\r\n\r\n this.#cleanupConnection()\r\n this.emit('exit', code)\r\n\r\n if (this.#autoRestart && wasRunning && this.#state !== 'destroyed') {\r\n this.start().catch(e => this.emit('error', e))\r\n }\r\n })\r\n\r\n return this\r\n } catch (err) {\r\n this.#state = 'errored'\r\n this.#cleanupConnection()\r\n throw err\r\n }\r\n }\r\n\r\n /** Send quit command to mpv (graceful shutdown) */\r\n async quit(code = 0) {\r\n if (!this.running) return\r\n try { await this.#send('quit', code) } catch {}\r\n await this.#waitForExit(3000)\r\n }\r\n\r\n /** Immediately kill the process and clean up. Cannot be restarted. */\r\n async destroy() {\r\n this.#autoRestart = false\r\n this.#state = 'destroyed'\r\n this.#abortController?.abort()\r\n await this.#forceKill()\r\n this.#cleanupConnection()\r\n this.#rejectAllPending('MPV destroyed')\r\n this.removeAllListeners()\r\n }\r\n\r\n // ── Playback ─────────────────────────────────────────────────\r\n\r\n /**\r\n * Load and play a file/URL.\r\n * @param {string} source - File path or URL\r\n * @param {object} [opts]\r\n * @param {'replace'|'append'|'append-play'|'insert-next'|'insert-at'} [opts.mode='replace']\r\n * @param {number} [opts.index] - Insertion index for 'insert-at' mode\r\n * @param {object} [opts.options] - Per-file mpv options (e.g. { start: '00:30' })\r\n */\r\n async load(source, { mode = 'replace', index, options } = {}) {\r\n this.#assertRunning()\r\n const args = ['loadfile', source, mode]\r\n if (index !== undefined) args.push(index)\r\n else if (options) args.push(-1)\r\n if (options) {\r\n args.push(Object.entries(options).map(([k, v]) => `${k}=${v}`).join(','))\r\n }\r\n return this.#send(...args)\r\n }\r\n\r\n /** Load a playlist file */\r\n async loadList(source, mode = 'replace') {\r\n this.#assertRunning()\r\n return this.#send('loadlist', source, mode)\r\n }\r\n\r\n /** Play a source, or resume playback if no source given */\r\n async play(source, opts) {\r\n if (source) return this.load(source, opts)\r\n return this.set('pause', false)\r\n }\r\n\r\n /** Pause playback */\r\n async pause() { return this.set('pause', true) }\r\n\r\n /** Resume playback */\r\n async resume() { return this.set('pause', false) }\r\n\r\n /** Toggle pause on/off */\r\n async togglePause() { return this.cycle('pause') }\r\n\r\n /** Stop playback */\r\n async stop(keepPlaylist = false) {\r\n this.#assertRunning()\r\n return this.#send('stop', keepPlaylist ? 'keep-playlist' : undefined)\r\n }\r\n\r\n // ── Seeking ──────────────────────────────────────────────────\r\n\r\n /**\r\n * Seek in the current file.\r\n * @param {number} target - Seconds or percent depending on mode\r\n * @param {'relative'|'absolute'|'absolute-percent'|'relative-percent'|'keyframes'|'exact'} [mode='relative']\r\n */\r\n async seek(target, mode = 'relative') {\r\n this.#assertRunning()\r\n return this.#send('seek', target, mode)\r\n }\r\n\r\n /** Seek to absolute position in seconds */\r\n async seekTo(seconds) { return this.seek(seconds, 'absolute') }\r\n\r\n /** Seek to percent of file */\r\n async seekPercent(pct) { return this.seek(pct, 'absolute-percent') }\r\n\r\n /** Undo last seek */\r\n async revertSeek(flags) {\r\n this.#assertRunning()\r\n return this.#send('revert-seek', flags)\r\n }\r\n\r\n /** Step forward/backward by frames */\r\n async frameStep(frames = 1, mode) {\r\n this.#assertRunning()\r\n return this.#send('frame-step', frames, mode)\r\n }\r\n\r\n /** Step backward one frame */\r\n async frameBackStep() {\r\n this.#assertRunning()\r\n return this.#send('frame-back-step')\r\n }\r\n\r\n /** Seek to subtitle (skip = 1 for next, -1 for prev, 0 for start of current) */\r\n async subSeek(skip) {\r\n this.#assertRunning()\r\n return this.#send('sub-seek', skip)\r\n }\r\n\r\n // ── Volume & Audio ───────────────────────────────────────────\r\n\r\n /** Get or set volume (0-150). No arg = get. */\r\n async volume(val) {\r\n if (val !== undefined) return this.set('volume', val)\r\n return this.get('volume')\r\n }\r\n\r\n /** Adjust volume by delta */\r\n async adjustVolume(delta) {\r\n return this.add('volume', delta)\r\n }\r\n\r\n /** Get or set mute. No arg = get. */\r\n async mute(val) {\r\n if (val !== undefined) return this.set('mute', !!val)\r\n return this.get('mute')\r\n }\r\n\r\n /** Toggle mute */\r\n async toggleMute() { return this.cycle('mute') }\r\n\r\n /** Get or set audio track. No arg = get. */\r\n async audioTrack(id) {\r\n if (id !== undefined) return this.set('aid', id)\r\n return this.get('aid')\r\n }\r\n\r\n /** Cycle audio tracks */\r\n async nextAudioTrack() { return this.cycle('aid') }\r\n\r\n /** Add an external audio file */\r\n async audioAdd(url, flags = 'select') {\r\n this.#assertRunning()\r\n return this.#send('audio-add', url, flags)\r\n }\r\n\r\n /** Remove audio track */\r\n async audioRemove(id) {\r\n this.#assertRunning()\r\n return this.#send('audio-remove', id)\r\n }\r\n\r\n // ── Video ────────────────────────────────────────────────────\r\n\r\n /** Get or set fullscreen. No arg = get. */\r\n async fullscreen(val) {\r\n if (val !== undefined) return this.set('fullscreen', !!val)\r\n return this.get('fullscreen')\r\n }\r\n\r\n /** Toggle fullscreen */\r\n async toggleFullscreen() { return this.cycle('fullscreen') }\r\n\r\n /** Get or set video track. No arg = get. */\r\n async videoTrack(id) {\r\n if (id !== undefined) return this.set('vid', id)\r\n return this.get('vid')\r\n }\r\n\r\n /** Add an external video file */\r\n async videoAdd(url, flags = 'select') {\r\n this.#assertRunning()\r\n return this.#send('video-add', url, flags)\r\n }\r\n\r\n /** Video filters — set/append/remove */\r\n async vf(operation, value) {\r\n this.#assertRunning()\r\n return this.#send('vf', operation, value)\r\n }\r\n\r\n /** Audio filters */\r\n async af(operation, value) {\r\n this.#assertRunning()\r\n return this.#send('af', operation, value)\r\n }\r\n\r\n // ── Subtitles ────────────────────────────────────────────────\r\n\r\n /** Get or set subtitle track. No arg = get. */\r\n async subTrack(id) {\r\n if (id !== undefined) return this.set('sid', id)\r\n return this.get('sid')\r\n }\r\n\r\n /** Cycle subtitle tracks */\r\n async nextSubTrack() { return this.cycle('sid') }\r\n\r\n /** Add an external subtitle file */\r\n async subAdd(url, flags = 'select', title, lang) {\r\n this.#assertRunning()\r\n const args = ['sub-add', url, flags]\r\n if (title !== undefined) args.push(title)\r\n if (lang !== undefined) args.push(lang)\r\n return this.#send(...args)\r\n }\r\n\r\n /** Remove subtitle track */\r\n async subRemove(id) {\r\n this.#assertRunning()\r\n return this.#send('sub-remove', id)\r\n }\r\n\r\n /** Reload subtitle track */\r\n async subReload(id) {\r\n this.#assertRunning()\r\n return this.#send('sub-reload', id)\r\n }\r\n\r\n /** Get or set subtitle delay in seconds */\r\n async subDelay(seconds) {\r\n if (seconds !== undefined) return this.set('sub-delay', seconds)\r\n return this.get('sub-delay')\r\n }\r\n\r\n /** Show/hide subtitles */\r\n async subVisibility(val) {\r\n if (val !== undefined) return this.set('sub-visibility', !!val)\r\n return this.get('sub-visibility')\r\n }\r\n\r\n // ── Speed ────────────────────────────────────────────────────\r\n\r\n /** Get or set playback speed. 1.0 = normal */\r\n async speed(val) {\r\n if (val !== undefined) return this.set('speed', val)\r\n return this.get('speed')\r\n }\r\n\r\n // ── Playlist ─────────────────────────────────────────────────\r\n\r\n /** Go to next playlist entry */\r\n async next(force = false) {\r\n this.#assertRunning()\r\n return this.#send('playlist-next', force ? 'force' : 'weak')\r\n }\r\n\r\n /** Go to previous playlist entry */\r\n async prev(force = false) {\r\n this.#assertRunning()\r\n return this.#send('playlist-prev', force ? 'force' : 'weak')\r\n }\r\n\r\n /** Jump to specific playlist index */\r\n async playIndex(index) {\r\n this.#assertRunning()\r\n return this.#send('playlist-play-index', index)\r\n }\r\n\r\n /** Append a file to the playlist. Starts playback if idle. */\r\n async append(source, options) {\r\n return this.load(source, { mode: 'append-play', options })\r\n }\r\n\r\n /** Insert a file after the current entry */\r\n async insertNext(source, options) {\r\n return this.load(source, { mode: 'insert-next', options })\r\n }\r\n\r\n /** Clear playlist (except current file) */\r\n async playlistClear() {\r\n this.#assertRunning()\r\n return this.#send('playlist-clear')\r\n }\r\n\r\n /** Remove playlist entry */\r\n async playlistRemove(index = 'current') {\r\n this.#assertRunning()\r\n return this.#send('playlist-remove', index)\r\n }\r\n\r\n /** Move playlist entry */\r\n async playlistMove(from, to) {\r\n this.#assertRunning()\r\n return this.#send('playlist-move', from, to)\r\n }\r\n\r\n /** Shuffle playlist */\r\n async playlistShuffle() {\r\n this.#assertRunning()\r\n return this.#send('playlist-shuffle')\r\n }\r\n\r\n /** Unshuffle (revert last shuffle) */\r\n async playlistUnshuffle() {\r\n this.#assertRunning()\r\n return this.#send('playlist-unshuffle')\r\n }\r\n\r\n /** Get the full playlist */\r\n async playlist() { return this.get('playlist') }\r\n\r\n /** Get playlist count */\r\n async playlistCount() { return this.get('playlist-count') }\r\n\r\n /** Get or set current playlist position */\r\n async playlistPos(index) {\r\n if (index !== undefined) return this.set('playlist-pos', index)\r\n return this.get('playlist-pos')\r\n }\r\n\r\n // ── A-B Loop ─────────────────────────────────────────────────\r\n\r\n /** Get or set A-B loop A point */\r\n async loopA(seconds) {\r\n if (seconds !== undefined) return this.set('ab-loop-a', seconds)\r\n return this.get('ab-loop-a')\r\n }\r\n\r\n /** Get or set A-B loop B point */\r\n async loopB(seconds) {\r\n if (seconds !== undefined) return this.set('ab-loop-b', seconds)\r\n return this.get('ab-loop-b')\r\n }\r\n\r\n /** Clear A-B loop (set both to 'no') */\r\n async loopClear() {\r\n await this.set('ab-loop-a', 'no')\r\n await this.set('ab-loop-b', 'no')\r\n }\r\n\r\n /** Get or set file loop. true = inf, number = count, false = no */\r\n async loop(val) {\r\n if (val !== undefined) {\r\n if (val === true) return this.set('loop-file', 'inf')\r\n if (val === false) return this.set('loop-file', 'no')\r\n return this.set('loop-file', val)\r\n }\r\n return this.get('loop-file')\r\n }\r\n\r\n async loopPlaylist(val) {\r\n if (val !== undefined) {\r\n if (val === true) return this.set('loop-playlist', 'inf')\r\n if (val === false) return this.set('loop-playlist', 'no')\r\n return this.set('loop-playlist', val)\r\n }\r\n return this.get('loop-playlist')\r\n }\r\n\r\n async position() { return this.get('time-pos') }\r\n async duration() { return this.get('duration') }\r\n async remaining() { return this.get('time-remaining') }\r\n async progress() { return this.get('percent-pos') }\r\n async title() { return this.get('media-title') }\r\n async filename() { return this.get('filename') }\r\n async path() { return this.get('path') }\r\n async metadata() { return this.get('metadata') }\r\n async tracks() { return this.get('track-list') }\r\n async chapters() { return this.get('chapter-list') }\r\n async paused() { return this.get('pause') }\r\n async idle() { return this.get('idle-active') }\r\n async width() { return this.get('width') }\r\n async height() { return this.get('height') }\r\n async fps() { return this.get('estimated-vf-fps') }\r\n async audioBitrate() { return this.get('audio-bitrate') }\r\n async audioCodec() { return this.get('audio-codec-name') }\r\n async videoCodec() { return this.get('video-codec-name') }\r\n async fileSize() { return this.get('file-size') }\r\n\r\n async chapter(n) {\r\n if (n !== undefined) return this.set('chapter', n)\r\n return this.get('chapter')\r\n }\r\n\r\n async status() {\r\n this.#assertRunning()\r\n const keys = [\r\n 'pause', 'time-pos', 'duration', 'percent-pos', 'volume', 'mute',\r\n 'speed', 'media-title', 'filename', 'playlist-pos', 'playlist-count',\r\n 'chapter', 'idle-active', 'loop-file', 'loop-playlist',\r\n ]\r\n const results = await Promise.allSettled(keys.map(k => this.get(k)))\r\n const status = {}\r\n for (let i = 0; i < keys.length; i++) {\r\n const key = keys[i].replace(/-./g, m => m[1].toUpperCase()) // kebab → camel\r\n status[key] = results[i].status === 'fulfilled' ? results[i].value : null\r\n }\r\n return status\r\n }\r\n\r\n /* 'subtitles' | 'video' | 'window' */\r\n async screenshot(mode = 'subtitles') {\r\n this.#assertRunning()\r\n return this.#send('screenshot', mode)\r\n }\r\n\r\n async screenshotToFile(path, mode = 'subtitles') {\r\n this.#assertRunning()\r\n return this.#send('screenshot-to-file', path, mode)\r\n }\r\n\r\n async showText(text, duration = 1000, level = 1) {\r\n this.#assertRunning()\r\n return this.#send('show-text', text, duration, level)\r\n }\r\n\r\n async showProgress() {\r\n this.#assertRunning()\r\n return this.#send('show-progress')\r\n }\r\n\r\n async applyProfile(name, mode = 'apply') {\r\n this.#assertRunning()\r\n return this.#send('apply-profile', name, mode)\r\n }\r\n\r\n async get(prop) {\r\n this.#assertRunning()\r\n return this.#send('get_property', prop)\r\n }\r\n\r\n async set(prop, value) {\r\n this.#assertRunning()\r\n return this.#send('set_property', prop, value)\r\n }\r\n\r\n async add(prop, value = 1) {\r\n this.#assertRunning()\r\n return this.#send('add', prop, value)\r\n }\r\n\r\n async cycle(prop, direction) {\r\n this.#assertRunning()\r\n return this.#send('cycle', prop, direction)\r\n }\r\n\r\n async multiply(prop, factor) {\r\n this.#assertRunning()\r\n return this.#send('multiply', prop, factor)\r\n }\r\n\r\n async toggle(prop) { return this.cycle(prop) }\r\n\r\n async command(...args) {\r\n this.#assertRunning()\r\n return this.#send(...args)\r\n }\r\n\r\n async observe(prop, fn) {\r\n this.#assertRunning()\r\n if (typeof fn !== 'function')\r\n throw new TypeError('Observer callback must be a function')\r\n\r\n if (this.#observers.has(prop)) {\r\n this.#observers.get(prop).fns.add(fn)\r\n return () => this.unobserve(prop, fn)\r\n }\r\n\r\n const id = ++this.#observeId\r\n const entry = { id, fns: new Set([fn]) }\r\n this.#observers.set(prop, entry)\r\n\r\n try {\r\n await this.#send('observe_property', id, prop)\r\n } catch (e) {\r\n this.#observers.delete(prop)\r\n throw e\r\n }\r\n\r\n return () => this.unobserve(prop, fn)\r\n }\r\n\r\n async observeMany(map) {\r\n const unsubs = await Promise.all(\r\n Object.entries(map).map(([prop, fn]) => this.observe(prop, fn))\r\n )\r\n return () => unsubs.forEach(u => u())\r\n }\r\n\r\n async unobserve(prop, fn) {\r\n const obs = this.#observers.get(prop)\r\n if (!obs) return\r\n\r\n if (fn) {\r\n obs.fns.delete(fn)\r\n if (obs.fns.size > 0) return\r\n }\r\n\r\n this.#observers.delete(prop)\r\n try { await this.#send('unobserve_property', obs.id) } catch {}\r\n }\r\n\r\n waitForEvent(event, timeout = 30000) {\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n this.off(event, handler)\r\n reject(new Error(`Timed out waiting for event: ${event}`))\r\n }, timeout)\r\n\r\n const handler = (data) => {\r\n clearTimeout(timer)\r\n resolve(data)\r\n }\r\n\r\n this.once(event, handler)\r\n })\r\n }\r\n\r\n async waitForProperty(prop, value, timeout = 30000) {\r\n try {\r\n const current = await this.get(prop)\r\n if (current === value) return current\r\n } catch {}\r\n\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n unsub()\r\n reject(new Error(`Timed out waiting for ${prop} = ${value}`))\r\n }, timeout)\r\n\r\n let unsub\r\n this.observe(prop, (val) => {\r\n if (val === value) {\r\n clearTimeout(timer)\r\n unsub?.()\r\n resolve(val)\r\n }\r\n }).then(u => { unsub = u })\r\n })\r\n }\r\n\r\n async [Symbol.asyncDispose]() {\r\n await this.destroy()\r\n }\r\n\r\n async #connectSocket() {\r\n const startTime = Bun.nanoseconds() / 1e6\r\n const timeout = this.#connectTimeout\r\n\r\n return new Promise((resolve, reject) => {\r\n let settled = false\r\n\r\n const attempt = () => {\r\n if (settled) return\r\n\r\n const elapsed = (Bun.nanoseconds() / 1e6) - startTime\r\n if (elapsed > timeout) {\r\n settled = true\r\n return reject(new Error(`Timed out connecting to mpv socket (${timeout}ms)`))\r\n }\r\n\r\n Bun.connect({\r\n unix: this.#socketPath,\r\n socket: {\r\n open: (s) => {\r\n if (settled) { s.end(); return }\r\n settled = true\r\n this.#socket = s\r\n s.unref()\r\n resolve()\r\n },\r\n data: (s, raw) => {\r\n const text = typeof raw === 'string' ? raw : new TextDecoder().decode(raw)\r\n this.#onData(text)\r\n },\r\n close: () => {\r\n if (!settled) {\r\n setTimeout(attempt, 20)\r\n return\r\n }\r\n },\r\n error: (s, err) => {\r\n if (!settled) {\r\n setTimeout(attempt, 20)\r\n return\r\n }\r\n this.emit('error', err)\r\n },\r\n end: () => {\r\n if (this.#state === 'running') {\r\n this.#forceKill()\r\n }\r\n },\r\n },\r\n }).catch(() => {\r\n if (!settled) setTimeout(attempt, 20)\r\n })\r\n }\r\n\r\n attempt()\r\n })\r\n }\r\n\r\n #send(...args) {\r\n const trace = new Error()\r\n Error.captureStackTrace(trace, this.#send)\r\n\r\n return new Promise((resolve, reject) => {\r\n const id = ++this.#requestId\r\n const request = {\r\n id,\r\n resolve,\r\n reject,\r\n args,\r\n message: JSON.stringify({\r\n request_id: id,\r\n command: args.filter(a => a !== undefined),\r\n }) + '\\n',\r\n }\r\n\r\n if (this.#socket && this.#socket.readyState === 1) {\r\n this.#write(request)\r\n } else {\r\n request._queued = true\r\n this.#requests.set(id, request)\r\n }\r\n }).catch(e => {\r\n trace.message = e.message || String(e)\r\n throw trace\r\n })\r\n }\r\n\r\n #write(request) {\r\n if (this.#socket) {\r\n this.#socket.write(request.message)\r\n request._queued = false\r\n this.#requests.set(request.id, request)\r\n }\r\n }\r\n\r\n #flushQueue() {\r\n for (const [id, req] of this.#requests) {\r\n if (req._queued) this.#write(req)\r\n }\r\n }\r\n\r\n #onData(chunk) {\r\n this.#buffer += chunk\r\n const lines = this.#buffer.split('\\n')\r\n this.#buffer = lines.pop()\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim()\r\n if (!trimmed) continue\r\n try {\r\n this.#handleMessage(JSON.parse(trimmed))\r\n } catch {\r\n this.emit('error', new Error(`Failed to parse mpv response: ${trimmed}`))\r\n }\r\n }\r\n }\r\n\r\n #handleMessage(msg) {\r\n if (msg.event) {\r\n if (msg.event === 'property-change') {\r\n const obs = this.#observers.get(msg.name)\r\n if (obs) {\r\n for (const fn of obs.fns) {\r\n try { fn(msg.data, msg.name) }\r\n catch (e) { this.emit('error', e) }\r\n }\r\n }\r\n }\r\n this.emit(msg.event, msg)\r\n return\r\n }\r\n\r\n const req = this.#requests.get(msg.request_id)\r\n if (!req) {\r\n if (msg.error !== 'success') {\r\n this.emit('error', Object.assign(new Error(msg.error), msg))\r\n }\r\n return\r\n }\r\n\r\n this.#requests.delete(msg.request_id)\r\n\r\n if (msg.error === 'success') {\r\n req.resolve(msg.data)\r\n } else {\r\n req.reject(new Error(\r\n `[${req.args.join(', ')}] failed: ${msg.error}`\r\n ))\r\n }\r\n }\r\n\r\n #resubscribeObservers() {\r\n for (const [prop, { id }] of this.#observers) {\r\n this.#send('observe_property', id, prop).catch(() => {})\r\n }\r\n }\r\n\r\n #assertRunning() {\r\n if (this.#state !== 'running') {\r\n throw new Error(`MPV is not running (state: ${this.#state}). Call .start() first.`)\r\n }\r\n }\r\n\r\n #cleanupConnection() {\r\n try { this.#socket?.end?.() } catch {}\r\n this.#socket = null\r\n this.#buffer = ''\r\n\r\n if (this.#socketPath && !IS_WIN) {\r\n unlink(this.#socketPath).catch(() => {})\r\n }\r\n }\r\n\r\n #rejectAllPending(reason) {\r\n for (const [id, req] of this.#requests) {\r\n req.reject(new Error(reason))\r\n }\r\n this.#requests.clear()\r\n }\r\n\r\n async #forceKill() {\r\n if (!this.#proc || this.#proc.killed) return\r\n this.#proc.kill()\r\n await this.#waitForExit(2000)\r\n }\r\n\r\n async #waitForExit(timeout) {\r\n if (!this.#proc) return\r\n try {\r\n await Promise.race([\r\n this.#proc.exited,\r\n new Promise((_, rej) => setTimeout(() => rej(), timeout)),\r\n ])\r\n } catch {\r\n try { this.#proc?.kill('SIGKILL') } catch {}\r\n }\r\n }\r\n}\r\n\r\nexport async function createMPV(options = {}) {\r\n const mpv = new MPV(options)\r\n await mpv.start()\r\n return mpv\r\n}\r\n\r\nconst instances = new Set()\r\nconst _origStart = MPV.prototype.start\r\nconst _origDestroy = MPV.prototype.destroy\r\n\r\nconst origEmit = MPV.prototype.emit\r\nMPV.prototype.emit = function(event, ...args) {\r\n if (event === 'start') instances.add(this)\r\n if (event === 'exit') instances.delete(this)\r\n return origEmit.call(this, event, ...args)\r\n}\r\n\r\nfunction cleanupAll() {\r\n for (const instance of instances) {\r\n try { instance.destroy() } catch {}\r\n }\r\n instances.clear()\r\n}\r\n\r\nprocess.on('exit', cleanupAll)\r\nprocess.on('SIGINT', () => { cleanupAll(); process.exit(130) })\r\nprocess.on('SIGTERM', () => { cleanupAll(); process.exit(143) })\r\n\r\nexport default createMPV\r\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "2762E2DBD2E1CE0264756E2164756E21",
9
+ "names": []
10
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "bun-mpv",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "Bun-native MPV player controller via JSON IPC. Zero dependencies. With built-in methods with a friendly config API.",
6
+ "keywords": ["mpv", "media", "player", "video", "audio", "bun", "ipc", "playback", "streaming", "playlist", "controller", "media-player", "mpv-ipc", "spawn"],
7
+ "module": "./dist/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "sideEffects": false,
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "bun run build.ts"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "author": "orielhaim",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/orielhaim/bun-mpv"
25
+ },
26
+ "devDependencies": {
27
+ "@types/bun": "latest"
28
+ },
29
+ "peerDependencies": {
30
+ "typescript": "^5"
31
+ }
32
+ }