├── .gitignore
├── README.md
├── media
└── mpv-types-lua-diptych-vscode-intellij.full.png
├── mpv
└── source
│ └── defaults.lua
└── types
├── init.lua
├── mp.d.lua.iml
├── mp.lua
└── mp
├── assdraw.lua
├── msg.lua
├── options.lua
└── utils.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .DS_Store
3 |
4 | #
5 | src/*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | mpv-types-lua
2 | ======
3 |
4 | Lua type declarations for mpv scripting.
5 |
6 | # FEATURES
7 |
8 | - Global typed `mp` object
9 |
10 | - Loading built in submodules with type information via `require` - e.g. `require('mp.msg')`
11 |
12 | - Function signatures for all built in functions
13 |
14 | - Documentation comments from mpv man pages
15 |
16 | ![][feature-image]
17 |
18 | # INSTALLATION
19 |
20 | ## Visual Studio Code
21 |
22 | ### Extension Configurations
23 |
24 | VSCode does not support Emmylua style type declarations out of the box, and requires one of the following extensions.
25 |
26 | #### Lua - sumneko.lua - [Marketplace][sumneko.lua] - [Github][sumneko.lua-repo]
27 |
28 | ``` javascript
29 | /* mpv builds with 5.1, 5.2, or luajit */
30 | "Lua.runtime.version": "Lua 5.1",
31 |
32 | /* Declarations can also be placed in workspace subdirectory */
33 | "Lua.workspace.library": {
34 | ".../path/to/mpv-lua-types/@types/": true
35 | },
36 |
37 | /* Recommended */
38 | "Lua.completion.enable": true,
39 | "Lua.signatureHelp.enable": true,
40 | "Lua.hover.enable": true,
41 | ```
42 |
43 | #### EmmyLua - tangzx.emmylua - [Marketplace][tangzx.emmylua] - [GitHub][tangzx.emmylua-repo]
44 |
45 | ``` javascript
46 | /* Declarations can also be placed in workspace subdirectory */
47 | "emmylua.source.roots": {
48 | ".../path/to/mpv-lua-types/@types/": true
49 | },
50 | ```
51 |
52 | #### LuaPanda - stuartwang.luapanda - [Marketplace][stuartwang.luapanda] - [GitHub][stuartwang.luapanda-repo]
53 |
54 | ``` javascript
55 | /* Declarations can also be placed in workspace subdirectory */
56 | "luaide-lite.apiFolders": [
57 | ".../path/to/mpv-types-lua/@types"
58 | ],
59 | "luaide-lite.core": "emmy",
60 | ```
61 |
62 | #### luaide-lite - wellshsu.luaide-lite - [Marketplace][wellshsu.luaide-lite] - [GitHub][wellshsu.luaide-lite-repo]
63 |
64 | - Declaration files must be in subdirectory of workspace.
65 |
66 | ``` json
67 | "lua_analyzer.codeLinting.luaVersion": "5.1",
68 | "lua_analyzer.codeLinting.enable": true
69 | ```
70 |
71 | ## Intellij
72 |
73 | ### Required Extensions
74 |
75 | - Emmylua - [Plugin Homepage][tangzx.emmylua-intellij] - [GitHub][tangzx.emmylua-repo]
76 |
77 | ### Installation
78 |
79 | Create a new Lua Zip Library and attach the the `@types` folder.
80 |
81 | # TODO
82 |
83 | - Create [lua library file](https://github.com/actboy168/lni)
84 |
85 | - Write documentation for `assdraw` submodule
86 |
87 | [feature-image]: media/mpv-types-lua-diptych-vscode-intellij.full.png
88 | [sumneko.lua]: https://marketplace.visualstudio.com/items?itemName=sumneko.lua
89 | [sumneko.lua-repo]: https://github.com/sumneko/lua-language-server
90 | [tangzx.emmylua]: https://marketplace.visualstudio.com/items?itemName=tangzx.emmylua
91 | [tangzx.emmylua-intellij]: https://plugins.jetbrains.com/plugin/9768-emmylua
92 | [tangzx.emmylua-repo]: https://github.com/EmmyLua/VSCode-EmmyLua
93 | [stuartwang.luapanda]: https://marketplace.visualstudio.com/items?itemName=stuartwang.luapanda
94 | [stuartwang.luapanda-repo]: https://github.com/Tencent/LuaPanda
95 | [wellshsu.luaide-lite]: https://marketplace.visualstudio.com/items?itemName=wellshsu.luaide-lite
96 | [wellshsu.luaide-lite-repo]: https://github.com/hsu2017/luaide-lite
--------------------------------------------------------------------------------
/media/mpv-types-lua-diptych-vscode-intellij.full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disco0/mpv-types-lua/ea1f7c2fb869691d716993d4aae659efc8a84932/media/mpv-types-lua-diptych-vscode-intellij.full.png
--------------------------------------------------------------------------------
/mpv/source/defaults.lua:
--------------------------------------------------------------------------------
1 | ---@meta
2 |
3 | -- Compatibility shim for lua 5.2/5.3
4 | unpack = unpack or table.unpack
5 |
6 | --region Non-source
7 | ---@type mp
8 | local mp = {}
9 | --endregion Non-source
10 |
11 | -- these are used internally by lua.c
12 | mp.UNKNOWN_TYPE.info = "this value is inserted if the C type is not supported"
13 | mp.UNKNOWN_TYPE.type = "UNKNOWN_TYPE"
14 |
15 | mp.ARRAY.info = "native array"
16 | mp.ARRAY.type = "ARRAY"
17 |
18 | mp.MAP.info = "native map"
19 | mp.MAP.type = "MAP"
20 |
21 | function mp.get_script_name()
22 | return mp.script_name
23 | end
24 |
25 | function mp.get_opt(key, def)
26 | local opts = mp.get_property_native("options/script-opts")
27 | local val = opts[key]
28 | if val == nil then
29 | val = def
30 | end
31 | return val
32 | end
33 |
34 | function mp.input_define_section(section, contents, flags)
35 | if flags == nil or flags == "" then
36 | flags = "default"
37 | end
38 | mp.commandv("define-section", section, contents, flags)
39 | end
40 |
41 | function mp.input_enable_section(section, flags)
42 | if flags == nil then
43 | flags = ""
44 | end
45 | mp.commandv("enable-section", section, flags)
46 | end
47 |
48 | function mp.input_disable_section(section)
49 | mp.commandv("disable-section", section)
50 | end
51 |
52 | function mp.get_mouse_pos()
53 | local m = mp.get_property_native("mouse-pos")
54 | return m.x, m.y
55 | end
56 |
57 | -- For dispatching script-binding. This is sent as:
58 | -- script-message-to $script_name $binding_name $keystate
59 | -- The array is indexed by $binding_name, and has functions like this as value:
60 | -- fn($binding_name, $keystate)
61 | local dispatch_key_bindings = {}
62 |
63 | local message_id = 0
64 | local function reserve_binding()
65 | message_id = message_id + 1
66 | return "__keybinding" .. tostring(message_id)
67 | end
68 |
69 | local function dispatch_key_binding(name, state, key_name, key_text)
70 | local fn = dispatch_key_bindings[name]
71 | if fn then
72 | fn(name, state, key_name, key_text)
73 | end
74 | end
75 |
76 | -- "Old", deprecated API
77 |
78 | -- each script has its own section, so that they don't conflict
79 | local default_section = "input_dispatch_" .. mp.script_name
80 |
81 | -- Set the list of key bindings. These will override the user's bindings, so
82 | -- you should use this sparingly.
83 | -- A call to this function will remove all bindings previously set with this
84 | -- function. For example, set_key_bindings({}) would remove all script defined
85 | -- key bindings.
86 | -- Note: the bindings are not active by default. Use enable_key_bindings().
87 | --
88 | -- list is an array of key bindings, where each entry is an array as follow:
89 | -- {key, callback_press, callback_down, callback_up}
90 | -- key is the key string as used in input.conf, like "ctrl+a"
91 | --
92 | -- callback can be a string too, in which case the following will be added like
93 | -- an input.conf line: key .. " " .. callback
94 | -- (And callback_down is ignored.)
95 | function mp.set_key_bindings(list, section, flags)
96 | local cfg = ""
97 | for i = 1, #list do
98 | local entry = list[i]
99 | local key = entry[1]
100 | local cb = entry[2]
101 | local cb_down = entry[3]
102 | local cb_up = entry[4]
103 | if type(cb) ~= "string" then
104 | local mangle = reserve_binding()
105 | dispatch_key_bindings[mangle] = function(name, state)
106 | local event = state:sub(1, 1)
107 | local is_mouse = state:sub(2, 2) == "m"
108 | local def = (is_mouse and "u") or "d"
109 | if event == "r" then
110 | return
111 | end
112 | if event == "p" and cb then
113 | cb()
114 | elseif event == "d" and cb_down then
115 | cb_down()
116 | elseif event == "u" and cb_up then
117 | cb_up()
118 | elseif event == def and cb then
119 | cb()
120 | end
121 | end
122 | cfg = cfg .. key .. " script-binding " ..
123 | mp.script_name .. "/" .. mangle .. "\n"
124 | else
125 | cfg = cfg .. key .. " " .. cb .. "\n"
126 | end
127 | end
128 | mp.input_define_section(section or default_section, cfg, flags)
129 | end
130 |
131 | function mp.enable_key_bindings(section, flags)
132 | mp.input_enable_section(section or default_section, flags)
133 | end
134 |
135 | function mp.disable_key_bindings(section)
136 | mp.input_disable_section(section or default_section)
137 | end
138 |
139 | function mp.set_mouse_area(x0, y0, x1, y1, section)
140 | mp.input_set_section_mouse_area(section or default_section, x0, y0, x1, y1)
141 | end
142 |
143 | -- "Newer" and more convenient API
144 |
145 | local key_bindings = {}
146 | local key_binding_counter = 0
147 | local key_bindings_dirty = false
148 |
149 | function mp.flush_keybindings()
150 | if not key_bindings_dirty then
151 | return
152 | end
153 | key_bindings_dirty = false
154 |
155 | for i = 1, 2 do
156 | local section, flags
157 | local def = i == 1
158 | if def then
159 | section = "input_" .. mp.script_name
160 | flags = "default"
161 | else
162 | section = "input_forced_" .. mp.script_name
163 | flags = "force"
164 | end
165 | local bindings = {}
166 | for k, v in pairs(key_bindings) do
167 | if v.bind and v.forced ~= def then
168 | bindings[#bindings + 1] = v
169 | end
170 | end
171 | table.sort(bindings, function(a, b)
172 | return a.priority < b.priority
173 | end)
174 | local cfg = ""
175 | for _, v in ipairs(bindings) do
176 | cfg = cfg .. v.bind .. "\n"
177 | end
178 | mp.input_define_section(section, cfg, flags)
179 | -- TODO: remove the section if the script is stopped
180 | mp.input_enable_section(section, "allow-hide-cursor+allow-vo-dragging")
181 | end
182 | end
183 |
184 | local function add_binding(attrs, key, name, fn, rp)
185 | if (type(name) ~= "string") and (name ~= nil) then
186 | rp = fn
187 | fn = name
188 | name = nil
189 | end
190 | rp = rp or ""
191 | if name == nil then
192 | name = reserve_binding()
193 | end
194 | local repeatable = rp == "repeatable" or rp["repeatable"]
195 | if rp["forced"] then
196 | attrs.forced = true
197 | end
198 | local key_cb, msg_cb
199 | if not fn then
200 | fn = function() end
201 | end
202 | if rp["complex"] then
203 | local key_states = {
204 | ["u"] = "up",
205 | ["d"] = "down",
206 | ["r"] = "repeat",
207 | ["p"] = "press",
208 | }
209 | key_cb = function(name, state, key_name, key_text)
210 | if key_text == "" then
211 | key_text = nil
212 | end
213 | fn({
214 | event = key_states[state:sub(1, 1)] or "unknown",
215 | is_mouse = state:sub(2, 2) == "m",
216 | key_name = key_name,
217 | key_text = key_text,
218 | })
219 | end
220 | msg_cb = function()
221 | fn({event = "press", is_mouse = false})
222 | end
223 | else
224 | key_cb = function(name, state)
225 | -- Emulate the same semantics as input.c uses for most bindings:
226 | -- For keyboard, "down" runs the command, "up" does nothing;
227 | -- for mouse, "down" does nothing, "up" runs the command.
228 | -- Also, key repeat triggers the binding again.
229 | local event = state:sub(1, 1)
230 | local is_mouse = state:sub(2, 2) == "m"
231 | if event == "r" and not repeatable then
232 | return
233 | end
234 | if is_mouse and (event == "u" or event == "p") then
235 | fn()
236 | elseif (not is_mouse) and (event == "d" or event == "r" or event == "p") then
237 | fn()
238 | end
239 | end
240 | msg_cb = fn
241 | end
242 | if key and #key > 0 then
243 | attrs.bind = key .. " script-binding " .. mp.script_name .. "/" .. name
244 | end
245 | attrs.name = name
246 | -- new bindings override old ones (but do not overwrite them)
247 | key_binding_counter = key_binding_counter + 1
248 | attrs.priority = key_binding_counter
249 | key_bindings[name] = attrs
250 | key_bindings_dirty = true
251 | dispatch_key_bindings[name] = key_cb
252 | mp.register_script_message(name, msg_cb)
253 | end
254 |
255 | function mp.add_key_binding(...)
256 | add_binding({forced=false}, ...)
257 | end
258 |
259 | function mp.add_forced_key_binding(...)
260 | add_binding({forced=true}, ...)
261 | end
262 |
263 | function mp.remove_key_binding(name)
264 | key_bindings[name] = nil
265 | dispatch_key_bindings[name] = nil
266 | key_bindings_dirty = true
267 | mp.unregister_script_message(name)
268 | end
269 |
270 | local timers = {}
271 |
272 | local timer_mt = {}
273 | timer_mt.__index = timer_mt
274 |
275 | function mp.add_timeout(seconds, cb)
276 | local t = mp.add_periodic_timer(seconds, cb)
277 | t.oneshot = true
278 | return t
279 | end
280 |
281 | function mp.add_periodic_timer(seconds, cb)
282 | local t = {
283 | timeout = seconds,
284 | cb = cb,
285 | oneshot = false,
286 | }
287 | setmetatable(t, timer_mt)
288 | t:resume()
289 | return t
290 | end
291 |
292 | function timer_mt.stop(t)
293 | if timers[t] then
294 | timers[t] = nil
295 | t.next_deadline = t.next_deadline - mp.get_time()
296 | end
297 | end
298 |
299 | function timer_mt.kill(t)
300 | timers[t] = nil
301 | t.next_deadline = nil
302 | end
303 | mp.cancel_timer = timer_mt.kill
304 |
305 | function timer_mt.resume(t)
306 | if not timers[t] then
307 | local timeout = t.next_deadline
308 | if timeout == nil then
309 | timeout = t.timeout
310 | end
311 | t.next_deadline = mp.get_time() + timeout
312 | timers[t] = t
313 | end
314 | end
315 |
316 | function timer_mt.is_enabled(t)
317 | return timers[t] ~= nil
318 | end
319 |
320 | -- Return the timer that expires next.
321 | local function get_next_timer()
322 | local best = nil
323 | for t, _ in pairs(timers) do
324 | if (best == nil) or (t.next_deadline < best.next_deadline) then
325 | best = t
326 | end
327 | end
328 | return best
329 | end
330 |
331 | function mp.get_next_timeout()
332 | local timer = get_next_timer()
333 | if not timer then
334 | return
335 | end
336 | local now = mp.get_time()
337 | return timer.next_deadline - now
338 | end
339 |
340 | -- Run timers that have met their deadline.
341 | -- Return: next absolute time a timer expires as number, or nil if no timers
342 | local function process_timers()
343 | while true do
344 | local timer = get_next_timer()
345 | if not timer then
346 | return
347 | end
348 | local now = mp.get_time()
349 | local wait = timer.next_deadline - now
350 | if wait > 0 then
351 | return wait
352 | else
353 | if timer.oneshot then
354 | timer:kill()
355 | else
356 | timer.next_deadline = now + timer.timeout
357 | end
358 | timer.cb()
359 | end
360 | end
361 | end
362 |
363 | local messages = {}
364 |
365 | function mp.register_script_message(name, fn)
366 | messages[name] = fn
367 | end
368 |
369 | function mp.unregister_script_message(name)
370 | messages[name] = nil
371 | end
372 |
373 | local function message_dispatch(ev)
374 | if #ev.args > 0 then
375 | local handler = messages[ev.args[1]]
376 | if handler then
377 | handler(unpack(ev.args, 2))
378 | end
379 | end
380 | end
381 |
382 | local property_id = 0
383 | local properties = {}
384 |
385 | function mp.observe_property(name, t, cb)
386 | local id = property_id + 1
387 | property_id = id
388 | properties[id] = cb
389 | mp.raw_observe_property(id, name, t)
390 | end
391 |
392 | function mp.unobserve_property(cb)
393 | for prop_id, prop_cb in pairs(properties) do
394 | if cb == prop_cb then
395 | properties[prop_id] = nil
396 | mp.raw_unobserve_property(prop_id)
397 | end
398 | end
399 | end
400 |
401 | local function property_change(ev)
402 | local prop = properties[ev.id]
403 | if prop then
404 | prop(ev.name, ev.data)
405 | end
406 | end
407 |
408 | -- used by default event loop (mp_event_loop()) to decide when to quit
409 | mp.keep_running = true
410 |
411 | local event_handlers = {}
412 |
413 | function mp.register_event(name, cb)
414 | local list = event_handlers[name]
415 | if not list then
416 | list = {}
417 | event_handlers[name] = list
418 | end
419 | list[#list + 1] = cb
420 | return mp.request_event(name, true)
421 | end
422 |
423 | function mp.unregister_event(cb)
424 | for name, sub in pairs(event_handlers) do
425 | local found = false
426 | for i, e in ipairs(sub) do
427 | if e == cb then
428 | found = true
429 | break
430 | end
431 | end
432 | if found then
433 | -- create a new array, just in case this function was called
434 | -- from an event handler
435 | local new = {}
436 | for i = 1, #sub do
437 | if sub[i] ~= cb then
438 | new[#new + 1] = sub[i]
439 | end
440 | end
441 | event_handlers[name] = new
442 | if #new == 0 then
443 | mp.request_event(name, false)
444 | end
445 | end
446 | end
447 | end
448 |
449 | -- default handlers
450 | mp.register_event("shutdown", function() mp.keep_running = false end)
451 | mp.register_event("client-message", message_dispatch)
452 | mp.register_event("property-change", property_change)
453 |
454 | -- called before the event loop goes back to sleep
455 | local idle_handlers = {}
456 |
457 | function mp.register_idle(cb)
458 | idle_handlers[#idle_handlers + 1] = cb
459 | end
460 |
461 | function mp.unregister_idle(cb)
462 | local new = {}
463 | for _, handler in ipairs(idle_handlers) do
464 | if handler ~= cb then
465 | new[#new + 1] = handler
466 | end
467 | end
468 | idle_handlers = new
469 | end
470 |
471 | -- sent by "script-binding"
472 | mp.register_script_message("key-binding", dispatch_key_binding)
473 |
474 | mp.msg = {
475 | log = mp.log,
476 | fatal = function(...) return mp.log("fatal", ...) end,
477 | error = function(...) return mp.log("error", ...) end,
478 | warn = function(...) return mp.log("warn", ...) end,
479 | info = function(...) return mp.log("info", ...) end,
480 | verbose = function(...) return mp.log("v", ...) end,
481 | debug = function(...) return mp.log("debug", ...) end,
482 | trace = function(...) return mp.log("trace", ...) end,
483 | }
484 |
485 | _G.print = mp.msg.info
486 |
487 | package.loaded["mp"] = mp
488 | package.loaded["mp.msg"] = mp.msg
489 |
490 | function mp.wait_event(t)
491 | local r = mp.raw_wait_event(t)
492 | if r and r.file_error and not r.error then
493 | -- compat; deprecated
494 | r.error = r.file_error
495 | end
496 | return r
497 | end
498 |
499 | _G.mp_event_loop = function()
500 | mp.dispatch_events(true)
501 | end
502 |
503 | local function call_event_handlers(e)
504 | local handlers = event_handlers[e.event]
505 | if handlers then
506 | for _, handler in ipairs(handlers) do
507 | handler(e)
508 | end
509 | end
510 | end
511 |
512 | mp.use_suspend = false
513 |
514 | local suspend_warned = false
515 |
516 | function mp.dispatch_events(allow_wait)
517 | local more_events = true
518 | if mp.use_suspend then
519 | if not suspend_warned then
520 | mp.msg.error("mp.use_suspend is now ignored.")
521 | suspend_warned = true
522 | end
523 | end
524 | while mp.keep_running do
525 | local wait = 0
526 | if not more_events then
527 | wait = process_timers() or 1e20 -- infinity for all practical purposes
528 | for _, handler in ipairs(idle_handlers) do
529 | handler()
530 | end
531 | -- Resume playloop - important especially if an error happened while
532 | -- suspended, and the error was handled, but no resume was done.
533 | mp.resume_all()
534 | if allow_wait ~= true then
535 | return
536 | end
537 | end
538 | local e = mp.wait_event(wait)
539 | more_events = false
540 | if e.event ~= "none" then
541 | call_event_handlers(e)
542 | more_events = true
543 | end
544 | end
545 | end
546 |
547 | mp.register_idle(mp.flush_keybindings)
548 |
549 | -- additional helpers
550 |
551 | function mp.osd_message(text, duration)
552 | if not duration then
553 | duration = "-1"
554 | else
555 | duration = tostring(math.floor(duration * 1000))
556 | end
557 | mp.commandv("show-text", text, duration)
558 | end
559 |
560 | local hook_table = {}
561 |
562 | local hook_mt = {}
563 | hook_mt.__index = hook_mt
564 |
565 | function hook_mt.cont(t)
566 | if t._id == nil then
567 | mp.msg.error("hook already continued")
568 | else
569 | mp.raw_hook_continue(t._id)
570 | t._id = nil
571 | end
572 | end
573 |
574 | function hook_mt.defer(t)
575 | t._defer = true
576 | end
577 |
578 | mp.register_event("hook", function(ev)
579 | local fn = hook_table[tonumber(ev.id)]
580 | local hookobj = {
581 | _id = ev.hook_id,
582 | _defer = false,
583 | }
584 | setmetatable(hookobj, hook_mt)
585 | if fn then
586 | fn(hookobj)
587 | end
588 | if (not hookobj._defer) and hookobj._id ~= nil then
589 | hookobj:cont()
590 | end
591 | end)
592 |
593 | function mp.add_hook(name, pri, cb)
594 | local id = #hook_table + 1
595 | hook_table[id] = cb
596 | -- The C API suggests using 0 for a neutral priority, but lua.rst suggests
597 | -- 50 (?), so whatever.
598 | mp.raw_hook_add(id, name, pri - 50)
599 | end
600 |
601 | local async_call_table = {}
602 | local async_next_id = 1
603 |
604 | function mp.command_native_async(node, cb)
605 | local id = async_next_id
606 | async_next_id = async_next_id + 1
607 | local res, err = mp.raw_command_native_async(id, node)
608 | if not res then
609 | cb(false, nil, err)
610 | return res, err
611 | end
612 | local t = {cb = cb, id = id}
613 | async_call_table[id] = t
614 | return t
615 | end
616 |
617 | mp.register_event("command-reply", function(ev)
618 | local id = tonumber(ev.id)
619 | local t = async_call_table[id]
620 | local cb = t.cb
621 | t.id = nil
622 | async_call_table[id] = nil
623 | if ev.error then
624 | cb(false, nil, ev.error)
625 | else
626 | cb(true, ev.result, nil)
627 | end
628 | end)
629 |
630 | function mp.abort_async_command(t)
631 | if t.id ~= nil then
632 | mp.raw_abort_async_command(t.id)
633 | end
634 | end
635 |
636 | local overlay_mt = {}
637 | overlay_mt.__index = overlay_mt
638 | local overlay_new_id = 0
639 |
640 | function mp.create_osd_overlay(format)
641 | overlay_new_id = overlay_new_id + 1
642 | local overlay = {
643 | format = format,
644 | id = overlay_new_id,
645 | data = "",
646 | res_x = 0,
647 | res_y = 720,
648 | }
649 | setmetatable(overlay, overlay_mt)
650 | return overlay
651 | end
652 |
653 | function overlay_mt.update(ov)
654 | local cmd = {}
655 | for k, v in pairs(ov) do
656 | cmd[k] = v
657 | end
658 | cmd.name = "osd-overlay"
659 | cmd.res_x = math.floor(cmd.res_x)
660 | cmd.res_y = math.floor(cmd.res_y)
661 | return mp.command_native(cmd)
662 | end
663 |
664 | function overlay_mt.remove(ov)
665 | mp.command_native {
666 | name = "osd-overlay",
667 | id = ov.id,
668 | format = "none",
669 | data = "",
670 | }
671 | end
672 |
673 | -- legacy API
674 | function mp.set_osd_ass(res_x, res_y, data)
675 | if not mp._legacy_overlay then
676 | mp._legacy_overlay = mp.create_osd_overlay("ass-events")
677 | end
678 | if mp._legacy_overlay.res_x ~= res_x or
679 | mp._legacy_overlay.res_y ~= res_y or
680 | mp._legacy_overlay.data ~= data
681 | then
682 | mp._legacy_overlay.res_x = res_x
683 | mp._legacy_overlay.res_y = res_y
684 | mp._legacy_overlay.data = data
685 | mp._legacy_overlay:update()
686 | end
687 | end
688 |
689 | function mp.get_osd_size()
690 | local prop = mp.get_property_native("osd-dimensions")
691 | return prop.w, prop.h, prop.aspect
692 | end
693 |
694 | function mp.get_osd_margins()
695 | local prop = mp.get_property_native("osd-dimensions")
696 | return prop.ml, prop.mt, prop.mr, prop.mb
697 | end
698 |
699 | local mp_utils = package.loaded["mp.utils"]
700 |
701 | function mp_utils.format_table(t, set)
702 | if not set then
703 | set = { [t] = true }
704 | end
705 | local res = "{"
706 | -- pretty expensive but simple way to distinguish array and map parts of t
707 | local keys = {}
708 | local vals = {}
709 | local arr = 0
710 | for i = 1, #t do
711 | if t[i] == nil then
712 | break
713 | end
714 | keys[i] = i
715 | vals[i] = t[i]
716 | arr = i
717 | end
718 | for k, v in pairs(t) do
719 | if not (type(k) == "number" and k >= 1 and k <= arr and keys[k]) then
720 | keys[#keys + 1] = k
721 | vals[#keys] = v
722 | end
723 | end
724 | for i = 1, #keys do
725 | if #res > 1 then
726 | res = res .. ", "
727 | end
728 | if i > arr then
729 | res = res .. mp_utils.to_string(keys[i], set) .. " = "
730 | end
731 | res = res .. mp_utils.to_string(vals[i], set)
732 | end
733 | res = res .. "}"
734 | return res
735 | end
736 |
737 | function mp_utils.to_string(v, set)
738 | if type(v) == "string" then
739 | return "\"" .. v .. "\""
740 | elseif type(v) == "table" then
741 | if set then
742 | if set[v] then
743 | return "[cycle]"
744 | end
745 | set[v] = true
746 | end
747 | return mp_utils.format_table(v, set)
748 | else
749 | return tostring(v)
750 | end
751 | end
752 |
753 | function mp_utils.getcwd()
754 | return mp.get_property("working-directory")
755 | end
756 |
757 | function mp_utils.format_bytes_humanized(b)
758 | local d = {"Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"}
759 | local i = 1
760 | while b >= 1024 do
761 | b = b / 1024
762 | i = i + 1
763 | end
764 | return string.format("%0.2f %s", b, d[i] and d[i] or "*1024^" .. (i-1))
765 | end
766 |
767 | function mp_utils.subprocess(t)
768 | local cmd = {}
769 | cmd.name = "subprocess"
770 | cmd.capture_stdout = true
771 | for k, v in pairs(t) do
772 | if k == "cancellable" then
773 | k = "playback_only"
774 | elseif k == "max_size" then
775 | k = "capture_size"
776 | end
777 | cmd[k] = v
778 | end
779 | local res, err = mp.command_native(cmd)
780 | if res == nil then
781 | -- an error usually happens only if parsing failed (or no args passed)
782 | res = {error_string = err, status = -1}
783 | end
784 | if res.error_string ~= "" then
785 | res.error = res.error_string
786 | end
787 | return res
788 | end
789 |
790 | function mp_utils.subprocess_detached(t)
791 | mp.commandv("run", unpack(t.args))
792 | end
793 |
794 | function mp_utils.shared_script_property_set(name, value)
795 | if value ~= nil then
796 | -- no such thing as change-list with mpv_node, so build a string value
797 | mp.commandv("change-list", "shared-script-properties", "append",
798 | name .. "=" .. value)
799 | else
800 | mp.commandv("change-list", "shared-script-properties", "remove", name)
801 | end
802 | end
803 |
804 | function mp_utils.shared_script_property_get(name)
805 | local map = mp.get_property_native("shared-script-properties")
806 | return map and map[name]
807 | end
808 |
809 | -- cb(name, value) on change and on init
810 | function mp_utils.shared_script_property_observe(name, cb)
811 | -- it's _very_ wasteful to observe the mpv core "super" property for every
812 | -- shared sub-property, but then again you shouldn't use this
813 | mp.observe_property("shared-script-properties", "native", function(_, val)
814 | cb(name, val and val[name])
815 | end)
816 | end
817 |
818 | return {}
819 |
--------------------------------------------------------------------------------
/types/init.lua:
--------------------------------------------------------------------------------
1 | ---@type mp
2 | _G.mp = nil
--------------------------------------------------------------------------------
/types/mp.d.lua.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/types/mp.lua:
--------------------------------------------------------------------------------
1 | -- mp.lua - Emmylua type annotations by disk0
2 | -- - Main repository:
3 |
4 | ---@alias MaybeError string | nil
5 | ---@alias AsyncCommmandCallback fun(success: boolean, result: any | nil, MaybeError: string | nil)
6 | ---@alias SharedScriptPropertyCallback fun(name: string, value: MpvPropertyType)
7 | ---@alias ObserverCallback fun(name: string)
8 | ---@alias EventName string | "'start-file'" | "'end-file'" | "'file-loaded'" | "'seek'" | "'playback-restart'" | "'idle'" | "'tick'" | "'shutdown'" | "'log-message'"
9 | ---@alias EndFileEventReason string | "'eof'" | "'stop'" | '"quit"' | "'MaybeError'" | "'redirect'" | "'unknown'"
10 | ---@alias LogMessageEventReason string | "'prefix'" | "'level'" | "'text'"
11 | ---@alias MessageLevel string | "'MaybeError'" | "'warn'" | "'info'" | "'v'" | "'debug'" | "'trace'"
12 | ---@alias MpvPropertyTypeLiteral string | '"none"' | '"native"' | '"boolean"' | '"string"' | '"number"'
13 | ---@alias MpvPropertyType boolean | string | number | nil | table
14 |
15 | ---
16 | --- - _`ass-events`_:
17 | ---
18 | --- The data parameter is a string. The string is split on the newline character. Every line is turned into the Text part of a Dialogue ASS event. Timing is unused (but behavior of timing dependent ASS tags may change in future mpv versions).
19 | ---
20 | --- Note that it's better to put multiple lines into data, instead of adding multiple OSD overlays.
21 | ---
22 | --- This provides 2 ASS Styles. OSD contains the text style as defined by the current `--osd-...` options. Default is similar, and contains style that OSD would have if all options were set to the default.
23 | ---
24 | --- In addition, the res_x and res_y options specify the value of the ASS PlayResX and PlayResY header fields. If res_y is set to 0, PlayResY is initialized to an arbitrary default value (but note that the default for this command is 720, not 0).
25 | --- If res_x is set to 0, PlayResX is set based on res_y such that a virtual ASS pixel has a square pixel aspect ratio.
26 | ---
27 | --- - _`none`_:
28 | ---
29 | --- Special value that causes the overlay to be removed. Most parameters other than id and format are mostly ignored.
30 | ---
31 | ---@alias OSD_Format '"ass-events"' | '"none"'
32 |
33 | ---@class OSD_Overlay_Data
34 | ---@field public id number
35 | ---@field public format OSD_Format
36 | ---@field public data string
37 | ---@field public res_x number
38 | ---@field public res_y number
39 |
40 | ---@class OSD_Overlay : OSD_Overlay_Data
41 | ---@field public update fun(): nil
42 | ---@field public remove fun(): nil
43 |
44 | ---
45 | --- Callback type for functions passed to `mp.register_event`.
46 | ---
47 | ---@alias EventHandler fun(event: EventName)
48 |
49 | ---@class AsyncCommandKey
50 | ---@field private __AsyncCommandKey userdata @ NOTE: This field is a branding only present in declarations, and does not exist at runtime.
51 |
52 | ---@class mp
53 | local mp = {}
54 |
55 | --region Commands
56 |
57 | ---
58 | --- Run the given command. This is similar to the commands used in input.conf.
59 | --- See _`List of Input Commands`_.
60 | ---
61 | --- By default, this will show something on the OSD (depending on the command),
62 | --- as if it was used in `input.conf`. See `Input Command Prefixes` how
63 | --- to influence OSD usage per command.
64 | ---
65 | --- Returns `true` on success, or `nil, error` on error.
66 | ---
67 | ---@param command string
68 | ---@return boolean | boolean
69 | ---@return MaybeError? CommandError
70 | function mp.command(command) end
71 |
72 | ---
73 | --- Similar to `mp.command`, but pass each command argument as separate
74 | --- parameter. This has the advantage that you don't have to care about
75 | --- quoting and escaping in some cases.
76 | ---
77 | --- Example:
78 | ---
79 | --- ``` lua
80 | --- mp.command("loadfile " .. filename .. " append")
81 | --- mp.commandv("loadfile", filename, "append")
82 | --- ```
83 | ---
84 | --- These two commands are equivalent, except that the first version breaks
85 | --- if the filename contains spaces or certain special characters.
86 | ---
87 | --- Note that properties are *not* expanded. You can use either `mp.command`,
88 | --- the `expand-properties` prefix, or the `mp.get_property` family of
89 | --- functions.
90 | ---
91 | --- Unlike `mp.command`, this will not use OSD by default either (except
92 | --- for some OSD-specific commands).
93 | ---
94 | ---@vararg string
95 | ---@return true | nil
96 | ---@return MaybeError?
97 | function mp.commandv(...) end
98 |
99 | ---
100 | --- Similar to `mp.commandv`, but pass the argument list as table. This has
101 | --- the advantage that in at least some cases, arguments can be passed as
102 | --- native types. It also allows you to use named argument.
103 | ---
104 | --- If the table is an array, each array item is like an argument in
105 | --- `mp.commandv()` (but can be a native type instead of a string).
106 | ---
107 | --- If the table contains string keys, it's interpreted as command with named
108 | --- arguments. This requires at least an entry with the key `name` to be
109 | --- present, which must be a string, and contains the command name. The special
110 | --- entry `_flags` is optional, and if present, must be an array of
111 | --- `Input Command Prefixes` to apply. All other entries are interpreted as
112 | --- arguments.
113 | ---
114 | --- Returns a result table on success (usually empty), or `def, error` on
115 | --- error. `def` is the second parameter provided to the function, and is
116 | --- `nil` if it's missing.
117 | ---
118 | ---@param command table
119 | ---@return boolean | 'true' | nil
120 | ---@return MaybeError?
121 | function mp.command_native(command) end
122 |
123 | ---
124 | --- Like `mp.command_native()`, but the command is ran asynchronously (as far
125 | --- as possible), and upon completion, fn is called. fn has two arguments:
126 | --- `fn(success, result, error)`. `success` is always a Boolean and is true
127 | --- if the command was successful, otherwise false. The second parameter is
128 | --- the result value (can be nil) in case of success, nil otherwise (as returned
129 | --- by `mp.command_native()`). The third parameter is the error string in case
130 | --- of an error, nil otherwise.
131 | ---
132 | --- Returns a table with undefined contents, which can be used as argument for
133 | --- `mp.abort_async_command`.
134 | ---
135 | --- If starting the command failed for some reason, `nil, error` is returned,
136 | --- and `fn` is called indicating failure, using the same error value.
137 | ---
138 | ---@overload fun(command: table, fn: AsyncCommmandCallback): AsyncCommandKey
139 | ---@param command table
140 | ---@return AsyncCommandKey
141 | function mp.command_native_async(command) end
142 |
143 | ---
144 | --- Abort a `mp.command_native_async` call. The argument is the return value
145 | --- of that command (which starts asynchronous execution of the command).
146 | --- Whether this works and how long it takes depends on the command and the
147 | --- situation. The abort call itself is asynchronous. Does not return anything.
148 | ---
149 | ---@param async_command_return AsyncCommandKey
150 | function mp.abort_async_command(async_command_return) end
151 |
152 | --endregion Commands
153 |
154 | --region Property Access
155 |
156 | ---
157 | --- Return the value of the given property as string. These are the same
158 | --- properties as used in input.conf. See _`Properties`_ for a list of
159 | --- properties. The returned string is formatted similar to `${=name}`
160 | --- (see _`Property Expansion`_).
161 | ---
162 | --- Returns the string on success, or `def, error` on error. `def` is the
163 | --- second parameter provided to the function, and is nil if it's missing.
164 | ---
165 | ---@overload fun(name: string, def: string): string, MaybeError
166 | ---@param name string
167 | ---@return string
168 | function mp.get_property(name) end
169 |
170 | ---
171 | --- Similar to `mp.get_property`, but return the property value formatted for
172 | --- OSD. This is the same string as printed with `${name}` when used in
173 | --- input.conf.
174 | ---
175 | --- Returns the string on success, or `def, error` on error. `def` is the
176 | --- second parameter provided to the function, and is an empty string if it's
177 | --- missing. Unlike `get_property()`, assigning the return value to a variable
178 | --- will always result in a string.
179 | ---
180 | ---@overload fun(name: string, def: D): string, MaybeError
181 | ---@param name string
182 | ---@return string
183 | function mp.get_property_osd(name) end
184 |
185 | ---
186 | --- Similar to `mp.get_property`, but return the property value as Boolean.
187 | ---
188 | --- Returns a Boolean on success, or `def, error` on error.
189 | ---
190 | ---@param name string
191 | ---@return boolean
192 | function mp.get_property_bool(name) end
193 |
194 | ---
195 | --- Similar to `mp.get_property`, but return the property value as number.
196 | ---
197 | --- Note that while Lua does not distinguish between integers and floats,
198 | --- mpv internals do. This function simply request a double float from mpv,
199 | --- and mpv will usually convert integer property values to float.
200 | ---
201 | --- Returns a number on success, or `def, error` on error.
202 | ---
203 | ---@generic D : number | nil
204 | ---@param name string
205 | ---@param def? D
206 | ---@return number | D, MaybeError
207 | function mp.get_property_number(name, def) end
208 |
209 | ---
210 | --- Similar to `mp.get_property`, but return the property value using the best
211 | --- Lua type for the property. Most time, this will return a string, Boolean,
212 | --- or number. Some properties (for example `chapter-list`) are returned as
213 | --- tables.
214 | ---
215 | --- Returns a value on success, or `def, error` on error. Note that `nil`
216 | --- might be a possible, valid value too in some corner cases.
217 | ---
218 | ---@generic D : string | boolean | number | table | nil
219 | ---@param name string
220 | ---@param def? D
221 | ---@return D, MaybeError
222 | function mp.get_property_native(name, def) end
223 |
224 | ---
225 | --- Set the given property to the given string value. See `mp.get_property`
226 | --- and `Properties` for more information about properties.
227 | ---
228 | --- Returns true on success, or `nil, error` on error.
229 | ---
230 | ---@param name string
231 | ---@param value string
232 | ---@return true | nil, MaybeError
233 | function mp.set_property(name, value) end
234 |
235 | ---
236 | --- Similar to `mp.set_property`, but sets `boolean` property of name `name` to `value`.
237 | ---
238 | ---@param name string
239 | ---@param value boolean
240 | ---@return true | nil, MaybeError
241 | function mp.set_property_bool(name, value) end
242 |
243 | ---
244 | --- Similar to `mp.set_property`, but set the given property to the given
245 | --- numeric value.
246 | ---
247 | --- Note that while Lua does not distinguish between integers and floats, mpv internals do.
248 | ---
249 | ---This function will test whether the number can be represented as integer, and if so, it will pass an integer value to mpv, otherwise a double float.
250 | ---
251 | ---@param name string
252 | ---@param value number
253 | ---@return true | nil, MaybeError
254 | function mp.set_property_number(name, value) end
255 |
256 | ---
257 | --- Similar to `mp.set_property`, but set the given property using its native
258 | --- type.
259 | ---
260 | --- Since there are several data types which cannot represented natively in Lua, this might not always work as expected. For example, while the Lua wrapper can do some guesswork to decide whether a Lua table is an array or a map, this would fail with empty tables. Also, there are not many properties for which it makes sense to use this, instead of `set_property`, `set_property_bool`, `set_property_number`. For these reasons, this function should probably be avoided for now, except for properties that use tables natively.
261 | ---
262 | ---@param name string
263 | ---@param value any
264 | ---@return true | nil, MaybeError
265 | function mp.set_property_native(name, value) end
266 |
267 | ---
268 | --- Search for the input filename in several paths. These include user and global config locations by default. Some platforms may implement additional platform related lookups (i.e.: macOS inside an application bundle).
269 | ---
270 | --- \- `mpv/options/path.h`
271 | ---
272 | ---@param filename string
273 | ---@return string | nil
274 | function mp.find_config_file(filename) end
275 |
276 | ---
277 | --- Return the current mpv internal time in seconds as a number. This is basically the system time, with an arbitrary offset.
278 | ---
279 | ---@return number
280 | function mp.get_time() end
281 |
282 | --endregion Property Access
283 |
284 | --region OSD Controls
285 |
286 | -- @TODO: Confirm return types
287 | ---
288 | --- Return width, height, and aspect ratio of on-screen display
289 | ---
290 | ---@return number, number, number
291 | function mp.get_osd_size() end
292 |
293 | ---
294 | --- Display a message at provided position.
295 | ---
296 | ---@param screenx integer
297 | ---@param screeny integer
298 | ---@param message string
299 | ---@return nil
300 | function mp.set_osd_ass(screenx, screeny, message) end
301 |
302 | ---
303 | --- Create an OSD overlay. This is a very thin wrapper around the `osd-overlay` command. The function returns a table, which mostly contains fields that will be passed to osd-overlay. The format parameter is used to initialize the format field. The data field contains the text to be used as overlay. For details, see the `osd-overlay` command.
304 | ---
305 | --- In addition, it provides the following methods:
306 | ---
307 | --- - _`update()`_:
308 | ---
309 | --- Commit the OSD overlay to the screen, or in other words, run the `osd-overlay` command with the current fields of the overlay table. Returns the result of the osd-overlay command itself.
310 | ---
311 | --- - _`remove()`_:
312 | ---
313 | --- Remove the overlay from the screen. A update() call will add it again.
314 | ---
315 | --- Example:
316 | ---
317 | --- ``` lua
318 | --- ov = mp.create_osd_overlay("ass-events")
319 | --- ov.data = "{\\an5}{\\b1}hello world!"
320 | --- ov:update()
321 | --- ```
322 | ---
323 | --- The advantage of using this wrapper (as opposed to running `osd-overlay` directly) is that the `id` field is allocated automatically.
324 | ---
325 | ---@param format OSD_Format
326 | ---@return OSD_Overlay
327 | function mp.create_osd_overlay(format) end
328 |
329 | --endregion OSD Controls
330 |
331 | --region Key Bindings
332 |
333 | ---@class KeybindFlags
334 | ---@field public repeatable boolean | nil @ If set to `true`, enables key repeat for this specific binding.
335 | ---@field public complex boolean | nil
336 |
337 | ---
338 | --- Register callback to be run on a key binding. The binding will be mapped to the given `key`, which is a string describing the physical key. This uses the same key names as in input.conf, and also allows combinations (e.g. `ctrl+a`). If the key is empty or `nil`, no physical key is registered, but the user still can create own bindings (see below).
339 | ---
340 | --- After calling this function, key presses will cause the function `fn` to be called (unless the user remapped the key with another binding).
341 | ---
342 | --- The `name` argument should be a short symbolic string. It allows the user to remap the key binding via input.conf using the `script-message` command, and the name of the key binding (see below for an example). The name should be unique across other bindings in the same script - if not, the previous binding with the same name will be overwritten. You can omit the name, in which case a random name is generated internally. (Omitting works as follows: either pass `nil` for `name`, or pass the `fn` argument in place of the name. The latter is not recommended and is handled for compatibility only).
343 | ---
344 | --- The last argument is used for optional flags. This is a table, which can have the following entries:
345 | ---
346 | --- - `repeatable`
347 | --- If set to `true`, enables key repeat for this specific binding.
348 | ---
349 | --- - `complex`
350 | --- If set to `true`, then `fn` is called on both key up and down events (as well as key repeat, if enabled), with the first argument being a table. This table has the following entries (and may contain undocumented ones):
351 | ---
352 | --- - `event`
353 | --- Set to one of the strings `down`, `repeat`, `up` or `press` (the latter if key up/down can't be tracked).
354 | ---
355 | --- - `is_mouse`
356 | --- Boolean Whether the event was caused by a mouse button.
357 | ---
358 | --- - `key_name`
359 | --- The name of they key that triggered this, or `nil` if invoked artificially. If the key name is unknown, it's an empty string.
360 | ---
361 | --- - `key_text`
362 | --- Text if triggered by a text key, otherwise `nil`. See description of `script-binding` command for details (this field is equivalent to the 5th argument).
363 | ---
364 | --- Internally, key bindings are dispatched via the `script-message-to` or `script-binding` input commands and `mp.register_script_message`.
365 | ---
366 | --- Trying to map multiple commands to a key will essentially prefer a random binding, while the other bindings are not called. It is guaranteed that user defined bindings in the central input.conf are preferred over bindings added with this function (but see `mp.add_forced_key_binding`).
367 | ---
368 | --- Example:
369 | ---
370 | --- ```lua
371 | --- function something_handler()
372 | --- print("the key was pressed")
373 | --- end
374 | --- mp.add_key_binding("x", "something", something_handler)
375 | --- ```
376 | ---
377 | --- This will print the message `the key was pressed` when `x` was pressed.
378 | ---
379 | --- The user can remap these key bindings. Then the user has to put the following into their input.conf to remap the command to the `y` key:
380 | --- ```
381 | --- y script-binding something
382 | --- ```
383 | ---
384 | --- This will print the message when the key `y` is pressed. (`x` will still work, unless the user remaps it.)
385 | ---
386 | --- You can also explicitly send a message to a named script only. Assume the above script was using the filename `fooscript.lua`:
387 | ---
388 | --- ```
389 | --- y script-binding fooscript/something
390 | --- ```
391 | ---
392 | ---@param key KeybindInputKey
393 | ---@param name string
394 | ---@param fn function
395 | ---@param rp KeybindFlags | nil
396 | function mp.add_key_binding(key, name, fn, rp) end
397 |
398 | ---
399 | --- This works almost the same as `mp.add_key_binding`, but registers the key binding in a way that will overwrite the user's custom bindings in their input.conf (`mp.add_key_binding` overwrites default key bindings only, but not those by the user's `input.conf`).
400 | ---
401 | ---@overload fun(key: string, name: string, fn: function)
402 | ---@param key KeybindInputKey
403 | ---@param name string
404 | ---@param fn function
405 | ---@param rp KeybindFlags | nil
406 | function mp.add_forced_key_binding(key, name, fn, rp) end
407 |
408 | ---
409 | --- Remove a key binding added with `mp.add_key_binding` or
410 | --- `mp.add_forced_key_binding`. Use the same name as you used when adding
411 | --- the bindings. It's not possible to remove bindings for which you omitted
412 | --- the name.
413 | ---
414 | ---@param name string
415 | function mp.remove_key_binding(name) end
416 |
417 | ---@alias BindingFlags string | "'allow-vo-dragging'" | "'allow-hide-cursor'" | "'exclusive'" | "'exclusive+allow-hide-cursor'" | "'exclusive+allow-vo-dragging'" | "'allow-hide-cursor+allow-vo-dragging'" | "'exclusive+allow-hide-cursor+allow-vo-dragging'"
418 |
419 | ---
420 | --- Enable keybindings declared in section `section`. The flags parameter can be a combination (separated by +) of the following flags:
421 | ---
422 | --- - _``_
423 | --- All sections enabled before the newly enabled section are disabled. They will be re-enabled as soon as all exclusive sections above them are removed. In other words, the new section shadows all previous sections.
424 | ---
425 | --- - _``_
426 | --- _This feature can't be used through the public API_.
427 | ---
428 | --- - _``_
429 | --- _This feature can't be used through the public API_.
430 | ---
431 | ---@overload fun(section: string)
432 | ---@param section string
433 | ---@param flags BindingFlags | nil
434 | function mp.enable_key_bindings(section, flags) end
435 |
436 | --region Keys
437 |
438 | ---@alias KeybindInputKey string | KeybindInputSpecial
439 |
440 | ---@alias KeybindInputSpecial
441 | ---| '"ANY_UNICODE"'
442 | ---| '"AXIS_DOWN"'
443 | ---| '"AXIS_LEFT"'
444 | ---| '"AXIS_RIGHT"'
445 | ---| '"AXIS_UP"'
446 | ---| '"BS"'
447 | ---| '"CANCEL"'
448 | ---| '"CHANNEL_DOWN"'
449 | ---| '"CHANNEL_UP"'
450 | ---| '"CLOSE_WIN"'
451 | ---| '"DEL"'
452 | ---| '"DOWN"'
453 | ---| '"END"'
454 | ---| '"ENTER"'
455 | ---| '"ESC"'
456 | ---| '"F1"'
457 | ---| '"F10"'
458 | ---| '"F11"'
459 | ---| '"F12"'
460 | ---| '"F2"'
461 | ---| '"F3"'
462 | ---| '"F4"'
463 | ---| '"F5"'
464 | ---| '"F6"'
465 | ---| '"F7"'
466 | ---| '"F8"'
467 | ---| '"F9"'
468 | ---| '"FAVORITES"'
469 | ---| '"FORWARD"'
470 | ---| '"GAMEPAD_ACTION_DOWN"'
471 | ---| '"GAMEPAD_ACTION_LEFT"'
472 | ---| '"GAMEPAD_ACTION_RIGHT"'
473 | ---| '"GAMEPAD_ACTION_UP"'
474 | ---| '"GAMEPAD_BACK"'
475 | ---| '"GAMEPAD_DPAD_DOWN"'
476 | ---| '"GAMEPAD_DPAD_LEFT"'
477 | ---| '"GAMEPAD_DPAD_RIGHT"'
478 | ---| '"GAMEPAD_DPAD_UP"'
479 | ---| '"GAMEPAD_LEFT_SHOULDER"'
480 | ---| '"GAMEPAD_LEFT_STICK"'
481 | ---| '"GAMEPAD_LEFT_STICK_DOWN"'
482 | ---| '"GAMEPAD_LEFT_STICK_LEFT"'
483 | ---| '"GAMEPAD_LEFT_STICK_RIGHT"'
484 | ---| '"GAMEPAD_LEFT_STICK_UP"'
485 | ---| '"GAMEPAD_LEFT_TRIGGER"'
486 | ---| '"GAMEPAD_MENU"'
487 | ---| '"GAMEPAD_RIGHT_SHOULDER"'
488 | ---| '"GAMEPAD_RIGHT_STICK"'
489 | ---| '"GAMEPAD_RIGHT_STICK_DOWN"'
490 | ---| '"GAMEPAD_RIGHT_STICK_LEFT"'
491 | ---| '"GAMEPAD_RIGHT_STICK_RIGHT"'
492 | ---| '"GAMEPAD_RIGHT_STICK_UP"'
493 | ---| '"GAMEPAD_RIGHT_TRIGGER"'
494 | ---| '"GAMEPAD_START"'
495 | ---| '"HOME"'
496 | ---| '"HOMEPAGE"'
497 | ---| '"IDEOGRAPHIC_SPACE"'
498 | ---| '"INS"'
499 | ---| '"KP0"'
500 | ---| '"KP1"'
501 | ---| '"KP2"'
502 | ---| '"KP3"'
503 | ---| '"KP4"'
504 | ---| '"KP5"'
505 | ---| '"KP6"'
506 | ---| '"KP7"'
507 | ---| '"KP8"'
508 | ---| '"KP9"'
509 | ---| '"KP_DEC"'
510 | ---| '"KP_DEL"'
511 | ---| '"KP_ENTER"'
512 | ---| '"KP_INS"'
513 | ---| '"LEFT"'
514 | ---| '"MAIL"'
515 | ---| '"MBTN10"'
516 | ---| '"MBTN11"'
517 | ---| '"MBTN12"'
518 | ---| '"MBTN13"'
519 | ---| '"MBTN14"'
520 | ---| '"MBTN15"'
521 | ---| '"MBTN16"'
522 | ---| '"MBTN17"'
523 | ---| '"MBTN18"'
524 | ---| '"MBTN19"'
525 | ---| '"MBTN9"'
526 | ---| '"MBTN_BACK"'
527 | ---| '"MBTN_FORWARD"'
528 | ---| '"MBTN_LEFT"'
529 | ---| '"MBTN_LEFT_DBL"'
530 | ---| '"MBTN_MID"'
531 | ---| '"MBTN_MID_DBL"'
532 | ---| '"MBTN_RIGHT"'
533 | ---| '"MBTN_RIGHT_DBL"'
534 | ---| '"MENU"'
535 | ---| '"MOUSE_BTN0"'
536 | ---| '"MOUSE_BTN0_DBL"'
537 | ---| '"MOUSE_BTN1"'
538 | ---| '"MOUSE_BTN10"'
539 | ---| '"MOUSE_BTN11"'
540 | ---| '"MOUSE_BTN12"'
541 | ---| '"MOUSE_BTN13"'
542 | ---| '"MOUSE_BTN14"'
543 | ---| '"MOUSE_BTN15"'
544 | ---| '"MOUSE_BTN16"'
545 | ---| '"MOUSE_BTN17"'
546 | ---| '"MOUSE_BTN18"'
547 | ---| '"MOUSE_BTN19"'
548 | ---| '"MOUSE_BTN1_DBL"'
549 | ---| '"MOUSE_BTN2"'
550 | ---| '"MOUSE_BTN2_DBL"'
551 | ---| '"MOUSE_BTN3"'
552 | ---| '"MOUSE_BTN4"'
553 | ---| '"MOUSE_BTN5"'
554 | ---| '"MOUSE_BTN6"'
555 | ---| '"MOUSE_BTN7"'
556 | ---| '"MOUSE_BTN8"'
557 | ---| '"MOUSE_BTN9"'
558 | ---| '"MOUSE_ENTER"'
559 | ---| '"MOUSE_LEAVE"'
560 | ---| '"MOUSE_MOVE"'
561 | ---| '"MUTE"'
562 | ---| '"NEXT"'
563 | ---| '"PAUSE"'
564 | ---| '"PAUSEONLY"'
565 | ---| '"PGDWN"'
566 | ---| '"PGUP"'
567 | ---| '"PLAY"'
568 | ---| '"PLAYONLY"'
569 | ---| '"PLAYPAUSE"'
570 | ---| '"POWER"'
571 | ---| '"PREV"'
572 | ---| '"PRINT"'
573 | ---| '"RECORD"'
574 | ---| '"REWIND"'
575 | ---| '"RIGHT"'
576 | ---| '"SEARCH"'
577 | ---| '"SHARP"'
578 | ---| '"SLEEP"'
579 | ---| '"SPACE"'
580 | ---| '"STOP"'
581 | ---| '"TAB"'
582 | ---| '"UNMAPPED"'
583 | ---| '"UP"'
584 | ---| '"VOLUME_DOWN"'
585 | ---| '"VOLUME_UP"'
586 | ---| '"WHEEL_DOWN"'
587 | ---| '"WHEEL_LEFT"'
588 | ---| '"WHEEL_RIGHT"'
589 | ---| '"WHEEL_UP"'
590 | ---| '"WWW"'
591 | ---| '"XF86_NEXT"'
592 | ---| '"XF86_PAUSE"'
593 | ---| '"XF86_PREV"'
594 | ---| '"XF86_STOP"'
595 |
596 | --endregion Keys
597 |
598 | --endregion Key Bindings
599 |
600 | --region Event Handlers
601 |
602 | ---
603 | --- Call a specific function when an event happens. The event name is a string, and `fn` is a function value.
604 | ---
605 | --- Some events have associated data. This is put into a `table` and passed as argument to `fn`. The Lua table by default contains a `name` field, which is a `string` containing the event name. If the event has an error associated, the `error` field is set to a string describing the error, on success it's not set.
606 | ---
607 | --- If multiple functions are registered for the same event, they are run in registration order, which the first registered function running before all the other ones.
608 | ---
609 | --- Returns `true` if such an event exists, `false` otherwise.
610 | ---
611 | --- Events are notifications from player core to scripts. You can register an event handler with `mp.register_event`.
612 | ---
613 | --- Note that all scripts (and other parts of the player) receive events equally, and there's no such thing as blocking other scripts from receiving events.
614 | ---
615 | --- Example:
616 | --- ```lua
617 | --- function my_fn(event)
618 | --- print("start of playback!")
619 | --- end
620 | --- mp.register_event("file-loaded", my_fn)
621 | --- ```
622 | ---
623 | --- ## List of events
624 | ---
625 | --- - _`start-file`_
626 | --- Happens right before a new file is loaded. When you receive this, the player is loading the file (or possibly already done with it).
627 | ---
628 | --- - _`end-file`_
629 | --- Happens after a file was unloaded. Typically, the player will load the next file right away, or quit if this was the last file.
630 | --- The event has the `reason` field, which takes one of these values:
631 | ---
632 | --- - _`eof`_
633 | --- The file has ended. This can (but doesn't have to) include incomplete files or broken network connections under circumstances.
634 | ---
635 | --- - _`stop`_
636 | --- Playback was ended by a command.
637 | ---
638 | --- - _`quit`_
639 | --- Playback was ended by sending the quit command.
640 | ---
641 | --- - _`error`_
642 | --- An error happened. In this case, an `error` field is present with the error string.
643 | ---
644 | --- - _`redirect`_
645 | --- Happens with playlists and similar. For additional details see `MPV_END_FILE_REASON_REDIRECT` in the C API.
646 | ---
647 | --- - _`unknown`_
648 | --- Unknown. Normally does not happen unless the Lua API is out of sync with the C API (likewise, it could happen that your script gets reason strings that did not exist yet at the time your script was written).
649 | ---
650 | --- - _`file-loaded`_
651 | --- Happens after a file was loaded and begins playback.
652 | ---
653 | --- - _`seek`_
654 | --- Happens on seeking (this might include cases when the player seeks internally, even without user interaction—e.g. segment changes when playing Matroska files with ordered chapters).
655 | ---
656 | --- - _`playback-restart`_
657 | --- Start of playback after seek or after file was loaded.
658 | ---
659 | --- - _`idle`_
660 | --- Idle mode is entered. This happens when playback ended, and the player was started with `--idle` or `--force-window`. This mode is implicitly ended when the `start-file` or `shutdown` events happen.
661 | ---
662 | --- - _`tick`_
663 | --- Called after a video frame was displayed. This is a hack, and you should avoid using it. Use timers instead and maybe watch pausing/unpausing events to avoid wasting CPU when the player is paused.
664 | ---
665 | --- - _`shutdown`_
666 | --- Sent when the player quits, and the script should terminate. Normally handled automatically. See `Details on the script initialization and lifecycle`.
667 | ---
668 | --- - _`log-message`_
669 | --- Receives messages enabled with `mp.enable_messages`. The message data is contained in the table passed as first parameter to the event handler.
670 | --- The table contains, in addition to the default event fields, the following fields:
671 | ---
672 | --- - _`prefix`_
673 | --- The module prefix, identifies the sender of the message. This is what the terminal player puts in front of the message text when using the `--v` option, and is also what is used for `--msg-level`.
674 | ---
675 | --- - _`level`_
676 | --- The log level as string. See `msg.log` for possible log level names. Note that later versions of mpv might add new levels or remove (undocumented) existing ones.
677 | ---
678 | --- - _`text`_
679 | --- The log message. The text will end with a newline character. Sometimes it can contain multiple lines.
680 | --- Keep in mind that these messages are meant to be hints for humans. You should not parse them, and prefix/level/text of messages might change any time.
681 | ---
682 | --- - _`get-property-reply`_
683 | --- _Undocumented_ (not useful for Lua scripts).
684 | ---
685 | --- - _`set-property-reply`_
686 | --- _Undocumented_ (not useful for Lua scripts).
687 | ---
688 | --- - _`command-reply`_
689 | --- _Undocumented_ (not useful for Lua scripts).
690 | ---
691 | --- - _`client-message`_
692 | --- _Undocumented_ (used internally).
693 | ---
694 | --- - _`video-reconfig`_
695 | --- Happens on video output or filter reconfig.
696 | ---
697 | --- - _`audio-reconfig`_
698 | --- Happens on audio output or filter reconfig.
699 | ---
700 | --- The following events also happen, but are deprecated:
701 | --- - `tracks-changed`
702 | --- - `track-switched`
703 | --- - `pause`
704 | --- - `unpause`
705 | --- - `metadata-update`
706 | --- - `chapter-change`
707 | ---
708 | --- Use `mp.observe_property()` instead.
709 | ---
710 | ---@param name EventName
711 | ---@param cb EventHandler
712 | function mp.register_event(name, cb) end
713 |
714 | ---
715 | --- Undo `mp.register_event(..., fn)`. This removes all event handlers that
716 | --- are equal to the `fn` parameter. This uses normal Lua `==` comparison,
717 | --- so be careful when dealing with closures.
718 | ---
719 | ---@param cb function
720 | function mp.unregister_event(cb) end
721 |
722 | --endregion Event Handlers
723 |
724 | ---
725 | --- Watch a property for changes. If the property `name` is changed, then the function `fn(name)` will be called. `type` can be `nil`, or be set to one of `none`, `native`, `boolean`, `string`, or `number`.
726 | --- `none` is the same as `nil`. For all other values, the new value of the property will be passed as second argument to `fn`, using
727 | --- `mp.get_property_` to retrieve it. This means if `type` is for example `string`, `fn` is roughly called as in
728 | --- `fn(name, mp.get_property_string(name))`.
729 | ---
730 | --- If possible, change events are coalesced. If a property is changed a bunch of times in a row, only the last change triggers the change function. (The exact behavior depends on timing and other things.)
731 | ---
732 | --- In some cases the function is not called even if the property changes.
733 | --- This depends on the property, and it's a valid feature request to ask for better update handling of a specific property.
734 | ---
735 | --- If the `type` is `none` or `nil`, sporadic property change events are possible. This means the change function `fn` can be called even if the property doesn't actually change.
736 | ---
737 | --- You always get an initial change notification. This is meant to initialize the user's state to the current value of the property.
738 | ---
739 | ---@generic T
740 | ---@param name string
741 | ---@param type MpvPropertyTypeLiteral | nil
742 | ---@param fn ObserverCallback
743 | function mp.observe_property(name, type, fn) end
744 |
745 | ---
746 | ---@param name string
747 | ---@return MpvPropertyType
748 | function mp.shared_script_property_get(name) end
749 |
750 | ---
751 | --- Undo `mp.observe_property(..., fn)`. This removes all property handlers
752 | --- that are equal to the `fn` parameter. This uses normal Lua `==`
753 | --- comparison, so be careful when dealing with closures.
754 | ---
755 | ---@param fn function
756 | function mp.unobserve_property(fn) end
757 |
758 | --region Timer
759 |
760 | ---@type Timer[]
761 | local timers = {}
762 |
763 | --TODO: Confirm type of Timer.{cb,timeout} is actually whats defined
764 | --TODO: Figure out how to do something similar to type thinning
765 | ---@alias false boolean | 'false'
766 | ---@alias true boolean | 'true'
767 |
768 | ---
769 | ---@class Timer
770 | ---@field public cb function
771 | ---@field public timeout number
772 | ---@field public oneshot boolean
773 | ---@field private next_timeout number|nil
774 | local timer_mt = {}
775 |
776 | ---
777 | function timer_mt:kill() end
778 |
779 | ---
780 | function timer_mt:resume() end
781 |
782 | ---@return boolean
783 | function timer_mt:is_enabled() end
784 |
785 | ---@param timer Timer
786 | function mp.cancel_timer(timer) end
787 |
788 | ---
789 | --- Call the given function periodically. This is like `mp.add_timeout`, but the timer is re-added after the function fn is run.
790 | ---
791 | --- Returns a timer object. The timer object provides the following methods:
792 | ---
793 | --- - `stop()`
794 | --- Disable the timer. Does nothing if the timer is already disabled.
795 | --- This will remember the current elapsed time when stopping, so that `resume()` essentially unpauses the timer.
796 | ---
797 | --- - `kill()`
798 | --- Disable the timer. Resets the elapsed time. `resume()` will
799 | --- restart the timer.
800 | ---
801 | --- - `resume()`
802 | --- Restart the timer. If the timer was disabled with `stop()`, this will resume at the time it was stopped.
803 | --- If the timer was disabled with `kill()`, or if it's a previously fired one-shot timer (added with `add_timeout()`), this starts the timer from the beginning, using the initially configured timeout.
804 | ---
805 | --- - `is_enabled()`
806 | --- Whether the timer is currently enabled or was previously disabled (e.g. by `stop()` or `kill()`).
807 | ---
808 | --- - `timeout` (RW)
809 | --- This field contains the current timeout period. This value is not updated as time progresses. It's only used to calculate when the timer should fire next when the timer expires.
810 | --- If you write this, you can call `t:kill() ; t:resume()` to reset the current timeout to the new one. (`t:stop()` won't use the new timeout.)
811 | ---
812 | --- - `oneshot` (RW)
813 | --- Whether the timer is periodic (`false`) or fires just once (`true`). This value is used when the timer expires (but before the timer callback function fn is run).
814 | ---
815 | --- Note that these are methods, and you have to call them using `:` insteadof `.` (Refer to .)
816 | ---
817 | --- Example:
818 | --- ```lua
819 | --- seconds = 0
820 | --- timer = mp.add_periodic_timer(1, function()
821 | --- print("called every second")
822 | --- -- stop it after 10 seconds
823 | --- seconds = seconds + 1
824 | --- if seconds >= 10 then timer:kill() end
825 | --- end)
826 | --- ```
827 | ---
828 | ---@param seconds number
829 | ---@param cb function
830 | ---@return Timer
831 | function mp.add_periodic_timer(seconds, cb) end
832 |
833 | ---
834 | --- Call the given function fn when the given number of seconds has elapsed.
835 | --- Note that the number of seconds can be fractional. For now, the timer's resolution may be as low as 50 ms, although this will be improved in the future.
836 | ---
837 | --- This is a one-shot timer: it will be removed when it's fired.
838 | ---
839 | --- Returns a timer object. See `mp.add_periodic_timer` for details.
840 | ---
841 | ---@param seconds number
842 | ---@param cb function
843 | function mp.add_timeout(seconds, cb) end
844 |
845 | ---
846 | --- Return the timer that expires next.
847 | ---
848 | ---@return Timer
849 | local function get_next_timer() end
850 |
851 | ---
852 | --- Return the relative time in seconds when the next timer (`mp.add_timeout` and similar) expires.
853 | ---
854 | --- If there is no timer, return `nil`.
855 | ---
856 | ---@return number
857 | function mp.get_next_timeout() end
858 |
859 | ---
860 | --- Run timers that have met their deadline. Returns next absolute time a timer expires as number, or nil if no timers
861 | ---
862 | ---@return number|nil
863 | local function process_timers() end
864 |
865 | --endregion Timer
866 |
867 | ---
868 | --- Return a setting from the `--script-opts` option. It's up to the user and the script how this mechanism is used.
869 | ---
870 | --- Currently, all scripts can access this equally, so you should be careful about collisions.
871 | ---
872 | ---@param key string
873 | ---@param def string
874 | ---@return string
875 | function mp.get_opt(key, def) end
876 |
877 | ---
878 | --- Return the name of the current script. The name is usually made of the filename of the script, with directory and file extension removed. If there are several scripts which would have the same name, it's made unique by appending a number.
879 | ---
880 | --- Example: `mp.get_script_name('/path/to/fooscript.lua') --> 'fooscript'`
881 | ---
882 | ---@return string
883 | function mp.get_script_name() end
884 |
885 | ---
886 | --- Return the directory if this is a script packaged as directory (see Script location for a description), or return nothing if this is a single file script.
887 | ---
888 | ---@return string | nil
889 | function mp.get_script_directory() end
890 |
891 | ---
892 | --- Show an OSD message on the screen. `duration` is in seconds, and is optional (uses `--osd-duration` by default).
893 | ---
894 | ---@param message string
895 | ---@param duration number
896 | function mp.osd_message(message, duration) end
897 |
898 | --region Depreciated
899 |
900 | ---
901 | --- This function has been deprecated in mpv `0.21.0` and does nothing starting with mpv `0.23.0` (no replacement).
902 | ---
903 | ---@deprecated 0.21.0
904 | ---@param suspend any
905 | function mp.suspend(suspend) end
906 |
907 | ---
908 | --- This function has been deprecated in mpv `0.21.0` and does nothing starting
909 | --- with mpv `0.23.0` (no replacement).
910 | ---
911 | ---@deprecated 0.21.0
912 | ---@param resume any
913 | function mp.resume(resume) end
914 |
915 | ---
916 | --- This function has been deprecated in mpv `0.21.0` and does nothing starting
917 | --- with mpv `0.23.0` (no replacement).
918 | ---
919 | ---@deprecated
920 | ---@param resume_all any
921 | function mp.resume_all(resume_all) end
922 |
923 | ---
924 | --- Calls `mpv_get_wakeup_pipe()` and returns the read end of the wakeup
925 | --- pipe. This is deprecated, but still works. (See `client.h` for details.)
926 | ---
927 | ---@deprecated
928 | function mp.get_wakeup_pipe() end
929 |
930 | --endregion Depreciated
931 |
932 | ---
933 | --- This can be used to run custom event loops. If you want to have direct
934 | --- control what the Lua script does (instead of being called by the default
935 | --- event loop), you can set the global variable `mp_event_loop` to your
936 | --- own function running the event loop. From your event loop, you should call
937 | --- `mp.dispatch_events()` to dequeue and dispatch mpv events.
938 | ---
939 | --- If the `allow_wait` parameter is set to `true`, the function will block
940 | --- until the next event is received or the next timer expires. Otherwise (and
941 | --- this is the default behavior), it returns as soon as the event loop is
942 | --- emptied. It's strongly recommended to use `mp.get_next_timeout()` and
943 | --- `mp.get_wakeup_pipe()` if you're interested in properly working
944 | --- notification of new events and working timers.
945 | ---
946 | function mp.dispatch_events(dispatch_events) end
947 |
948 | ---
949 | --- Register an event loop idle handler. Idle handlers are called before the
950 | --- script goes to sleep after handling all new events. This can be used for
951 | --- example to delay processing of property change events: if you're observing
952 | --- multiple properties at once, you might not want to act on each property
953 | --- change, but only when all change notifications have been received.
954 | ---
955 | function mp.register_idle(register_idle) end
956 |
957 | ---
958 | --- Undo `mp.register_idle(fn)`. This removes all idle handlers that
959 | --- are equal to the `fn` parameter. This uses normal Lua `==` comparison,
960 | --- so be careful when dealing with closures.
961 | ---
962 | function mp.unregister_idle(unregister_idle) end
963 |
964 | ---
965 | --- Set the minimum log level of which mpv message output to receive. These
966 | --- messages are normally printed to the terminal. By calling this function,
967 | --- you can set the minimum log level of messages which should be received with
968 | --- the `log-message` event. See the description of this event for details.
969 | --- The level is a string, see `msg.log` for allowed log levels.
970 | ---
971 | ---@param level MessageLevel
972 | function mp.enable_messages(level) end
973 |
974 | --region Script Messages
975 |
976 | ---
977 | --- This is a helper to dispatch `script-message` or `script-message-to` invocations to Lua functions. `fn` is called if `script-message` or `script-message-to` (with this script as destination) is run with `name` as first parameter. The other parameters are passed to `fn`. If a message with the given name is already registered, it's overwritten.
978 | ---
979 | --- Used by `mp.add_key_binding`, so be careful about name collisions.
980 | ---
981 | ---@param name string
982 | ---@param fn function
983 | function mp.register_script_message(name, fn) end
984 |
985 | ---
986 | --- Undo a previous registration with `mp.register_script_message`. Does
987 | --- nothing if the `name` wasn't registered.
988 | ---
989 | ---@param name string
990 | function mp.unregister_script_message(name) end
991 |
992 | --endregion Script Messages
993 |
994 | ---@alias HookType
995 | ---| '"on_load"' # Called when a file is to be opened, before anything is actually done. For example, you could read and write the `stream-open-filename` property to redirect an URL to something else (consider support for streaming sites which rarely give the user a direct media URL), or you could set per-file options with by setting the property `file-local-options/