summaryrefslogtreecommitdiffstats
path: root/DOCS/man/javascript.rst
blob: 2ebdb158969de447cb9549f07b89fcde8247c141 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
JAVASCRIPT
==========

JavaScript support in mpv is near identical to its Lua support. Use this section
as reference on differences and availability of APIs, but otherwise you should
refer to the Lua documentation for API details and general scripting in mpv.

Example
-------

JavaScript code which leaves fullscreen mode when the player is paused:

::

    function on_pause_change(name, value) {
        if (value == true)
            mp.set_property("fullscreen", "no");
    }
    mp.observe_property("pause", "bool", on_pause_change);


Similarities with Lua
---------------------

mpv tries to load a script file as JavaScript if it has a ``.js`` extension, but
otherwise, the documented Lua options, script directories, loading, etc apply to
JavaScript files too.

Script initialization and lifecycle is the same as with Lua, and most of the Lua
functions at the modules ``mp``, ``mp.utils`` and ``mp.msg`` are available to
JavaScript with identical APIs - including running commands, getting/setting
properties, registering events/key-bindings/property-changes/hooks, etc.

Differences from Lua
--------------------

No need to load modules. ``mp``, ``mp.utils`` and ``mp.msg`` are preloaded, and
you can use e.g. ``var cwd = mp.utils.getcwd();`` without prior setup.
``mp.options`` is currently not implemented, but ``mp.get_opt(...)`` is.

Errors are slightly different. Where the Lua APIs return ``nil`` for error,
the JavaScript ones return ``undefined``. Where Lua returns ``something, error``
JavaScript returns only ``something`` - and makes ``error`` available via
``mp.last_error()``. Note that only some of the functions have this additional
``error`` value - typically the same ones which have it in Lua.

Standard APIs are preferred. For instance ``setTimeout`` and ``JSON.stringify``
are available, but ``mp.add_timeout`` and ``mp.utils.format_json`` are not.

No standard library. This means that interaction with anything outside of mpv is
limited to the available APIs, typically via ``mp.utils``. However, some file
functions were added, and CommonJS ``require`` is available too - where the
loaded modules have the same privileges as normal scripts.

Language features - ECMAScript 5
--------------------------------

The scripting backend which mpv currently uses is MuJS - a compatible minimal
ES5 interpreter. As such, ``String.substring`` is implemented for instance,
while the common but non-standard ``String.substr`` is not. Please consult the
MuJS pages on language features and platform support - http://mujs.com .

Unsupported Lua APIs and their JS alternatives
----------------------------------------------

``mp.add_timeout(seconds, fn)``  JS: ``id = setTimeout(fn, ms)``

``mp.add_periodic_timer(seconds, fn)``  JS: ``id = setInterval(fn, ms)``

``mp.register_idle(fn)``  JS: ``id = setTimeout(fn)``

``mp.unregister_idle(fn)``  JS:  ``clearTimeout(id)``

``utils.parse_json(str [, trail])``  JS: ``JSON.parse(str)``

``utils.format_json(v)``  JS: ``JSON.stringify(v)``

``utils.to_string(v)``  see ``dump`` below.

``mp.suspend()`` JS: none (deprecated).

``mp.resume()`` JS: none (deprecated).

``mp.resume_all()`` JS: none (deprecated).

``mp.get_next_timeout()`` see event loop below.

``mp.dispatch_events([allow_wait])`` see event loop below.

``mp.options`` module is not implemented currently for JS.

Scripting APIs - identical to Lua
---------------------------------

(LE) - Last-Error, indicates that ``mp.last_error()`` can be used after the
call to test for success (empty string) or failure (non empty reason string).
Otherwise, where the Lua APIs return ``nil`` on error, JS returns ``undefined``.

``mp.command(string)`` (LE)

``mp.commandv(arg1, arg2, ...)`` (LE)

``mp.command_native(table [,def])`` (LE)

``mp.get_property(name [,def])`` (LE)

``mp.get_property_osd(name [,def])`` (LE)

``mp.get_property_bool(name [,def])`` (LE)

``mp.get_property_number(name [,def])`` (LE)

``mp.get_property_native(name [,def])`` (LE)

``mp.set_property(name, value)`` (LE)

``mp.set_property_bool(name, value)`` (LE)

``mp.set_property_number(name, value)`` (LE)

``mp.set_property_native(name, value)`` (LE)

``mp.get_time()``

``mp.add_key_binding(key, name|fn [,fn [,flags]])``

``mp.add_forced_key_binding(...)``

``mp.remove_key_binding(name)``

``mp.register_event(name, fn)``

``mp.unregister_event(fn)``

``mp.observe_property(name, type, fn)``

``mp.unobserve_property(fn)``

``mp.get_opt(key)``

``mp.get_script_name()``

``mp.osd_message(text [,duration])``

``mp.get_wakeup_pipe()``

``mp.enable_messages(level)``

``mp.register_script_message(name, fn)``

``mp.unregister_script_message(name)``

``mp.msg.log(level, ...)``

``mp.msg.fatal(...)``

``mp.msg.error(...)``

``mp.msg.warn(...)``

``mp.msg.info(...)``

``mp.msg.verbose(...)``

``mp.msg.debug(...)``

``mp.msg.trace(...)``

``mp.utils.getcwd()`` (LE)

``mp.utils.readdir(path [, filter])`` (LE)

``mp.utils.file_info(path)`` (LE)

``mp.utils.split_path(path)``

``mp.utils.join_path(p1, p2)``

``mp.utils.subprocess(t)``

``mp.utils.subprocess_detached(t)``

``mp.add_hook(type, priority, fn)``

Additional utilities
--------------------

``mp.last_error()``
    If used after an API call which updates last error, returns an empty string
    if the API call succeeded, or a non-empty error reason string otherwise.

``Error.stack`` (string)
    When using ``try { ... } catch(e) { ... }``, then ``e.stack`` is the stack
    trace of the error - if it was created using the ``Error(...)`` constructor.

``print`` (global)
    A convenient alias to ``mp.msg.info``.

``dump`` (global)
    Like ``print`` but also expands objects and arrays recursively.

``mp.utils.getenv(name)``
    Returns the value of the host environment variable ``name``, or
    ``undefined`` if the variable is not defined.

``mp.utils.get_user_path(path)``
    Expands (mpv) meta paths like ``~/x``, ``~~/y``, ``~~desktop/z`` etc.
    ``read_file``, ``write_file`` and ``require`` already use this internaly.

``mp.utils.read_file(fname [,max])``
    Returns the content of file ``fname`` as string. If ``max`` is provided and
    not negative, limit the read to ``max`` bytes.

``mp.utils.write_file(fname, str)``
    (Over)write file ``fname`` with text content ``str``. ``fname`` must be
    prefixed with ``file://`` as simple protection against accidental arguments
    switch, e.g. ``mp.utils.write_file("file://~/abc.txt", "hello world")``.

Note: ``read_file`` and ``write_file`` throw on errors, allow text content only.

``mp.get_time_ms()``
    Same as ``mp.get_time()`` but in ms instead of seconds.

``mp.get_script_file()``
    Returns the file name of the current script.

``exit()`` (global)
    Make the script exit at the end of the current event loop iteration.
    Note: please remove added key bindings before calling ``exit()``.

``mp.utils.compile_js(fname, content_str)``
    Compiles the JS code ``content_str`` as file name ``fname`` (without loading
    anything from the filesystem), and returns it as a function. Very similar
    to a ``Function`` constructor, but shows at stack traces as ``fname``.

Timers (global)
---------------

The standard HTML/node.js timers are available:

``id = setTimeout(fn [,duration [,arg1 [,arg2...]]])``

``id = setTimeout(code_string [,duration])``

``clearTimeout(id)``

``id = setInterval(fn [,duration [,arg1 [,arg2...]]])``

``id = setInterval(code_string [,duration])``

``clearInterval(id)``

``setTimeout`` and ``setInterval`` return id, and later call ``fn`` (or execute
``code_string``) after ``duration`` ms. Interval also repeat every ``duration``.

``duration`` has a minimum and default value of 0, ``code_string`` is
a plain string which is evaluated as JS code, and ``[,arg1 [,arg2..]]`` are used
as arguments (if provided) when calling back ``fn``.

The ``clear...(id)`` functions cancel timer ``id``, and are irreversible.

Note: timers always call back asynchronously, e.g. ``setTimeout(fn)`` will never
call ``fn`` before returning. ``fn`` will be called either at the end of this
event loop iteration or at a later event loop iteration. This is true also for
intervals - which also never call back twice at the same event loop iteration.

Additionally, timers are processed after the event queue is empty, so it's valid
to use ``setTimeout(fn)`` instead of Lua's ``mp.register_idle(fn)``.

CommonJS modules and ``require(id)``
------------------------------------

CommonJS Modules are a standard system where scripts can export common functions
for use by other scripts. A module is a script which adds properties (functions,
etc) to its invisible ``exports`` object, which another script can access by
loading it with ``require(module-id)`` - which returns that ``exports`` object.

Modules and ``require`` are supported, standard compliant, and generally similar
to node.js. However, most node.js modules won't run due to missing modules such
as ``fs``, ``process``, etc, but some node.js modules with minimal dependencies
do work. In general, this is for mpv modules and not a node.js replacement.

A ``.js`` file extension is always added to ``id``, e.g. ``require("./foo")``
will load the file ``./foo.js`` and return its ``exports`` object.

An id is relative (to the script which ``require``'d it) if it starts with
``./`` or ``../``. Otherwise, it's considered a "top-level id" (CommonJS term).

Top level id is evaluated as absolute filesystem path if possible (e.g. ``/x/y``
or ``~/x``). Otherwise, it's searched at ``scripts/modules.js/`` in mpv config
dirs - in normal config search order. E.g. ``require("x")`` is searched as file
``x.js`` at those dirs, and id ``foo/x`` is searched as file ``foo/x.js``.

No ``global`` variable, but a module's ``this`` at its top lexical scope is the
global object - also in strict mode. If you have a module which needs ``global``
as the global object, you could do ``this.global = this;`` before ``require``.

Functions and variables declared at a module don't pollute the global object.

The event loop
--------------

The event loop poll/dispatch mpv events as long as the queue is not empty, then
processes the timers, then waits for the next event, and repeats this forever.

You could put this code at your script to replace the built-in event loop, and
also print every event which mpv sends to your script:

::

    function mp_event_loop() {
        var wait = 0;
        do {
            var e = mp.wait_event(wait);
            dump(e);  // there could be a lot of prints...
            if (e.event != "none") {
                mp.dispatch_event(e);
                wait = 0;
            } else {
                wait = mp.process_timers() / 1000;
            }
        } while (mp.keep_running);
    }


``mp_event_loop`` is a name which mpv tries to call after the script loads.
The internal implementation is similar to this (without ``dump`` though..).

``e = mp.wait_event(wait)`` returns when the next mpv event arrives, or after
``wait`` seconds if positive and no mpv events arrived. ``wait`` value of 0
returns immediately (with ``e.event == "none"`` if the queue is empty).

``mp.dispatch_event(e)`` calls back the handlers registered for ``e.event``,
if there are such (event handlers, property observers, script messages, etc).

``mp.process_timers()`` calls back the already-added, non-canceled due timers,
and returns the duration in ms till the next due timer (possibly 0), or -1 if
there are no pending timers. Must not be called recursively.

Note: ``exit()`` is also registered for the ``shutdown`` event, and its
implementation is a simple ``mp.keep_running = false``.