├── .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/