news 2026/9/11 5:57:05

如何为 mpv 编写第一个 JavaScript 脚本?JS 与 Lua 环境的关键差异

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何为 mpv 编写第一个 JavaScript 脚本?JS 与 Lua 环境的关键差异

如何为 mpv 编写第一个 JavaScript 脚本?JS 与 Lua 环境的关键差异

【免费下载链接】mpv🎥 Command line media player项目地址: https://gitcode.com/GitHub_Trending/mp/mpv

mpv 的脚本文档以 Lua 为主体,但 DOCS/man/javascript.rst 明确说明:JavaScript 支持与 Lua 支持"几乎完全一致",API 细节与通用脚本编写都参照 Lua 文档,只有少数加载方式、错误处理与语言特性上的差异。本文按照这条文档指引走一遍完整流程:确认当前 mpv 编译了 JavaScript 后端、把第一个.js脚本放到正确的位置、验证它确实被加载并生效,最后列出 JS 与 Lua 环境之间必须知道的关键差异。

先确认这个 mpv 带 JavaScript 后端

JavaScript 后端是可选的构建特性。在 meson.options 中它定义为javascript选项,类型是feature,默认值auto;meson.build 中会查找 MuJS 依赖,版本要求不低于 1.0.0,找不到则该特性为否。构建配置完成后,mpv 的构建摘要(summary)里会打印javascript: yes/no一项,用它核对当前构建是否启用。

运行时也有直接的症状:把脚本交给 mpv 加载时,若扩展名无法匹配到任何已编译的脚本后端,控制台会打印一条错误——按 DOCS/man/lua.rst "Script location" 一节的说法,要么是扩展名拼错了,要么是对应后端没有编译进你的 mpv。源码中的错误串为Can't load unknown script: <文件>(见 player/scripting.c)。对.js文件看到这条错误,就说明这个 mpv 缺 JavaScript 后端,需要先解决编译问题,后面所有步骤都以此为前提。

mpv 如何找到并加载 JS 脚本

javascript.rst 的第一条规则:脚本文件带.js扩展名时,mpv 会尝试把它作为 JavaScript 加载;除此之外,文档中所有 Lua 的脚本选项、脚本目录、加载方式同样适用于 JavaScript 文件。具体有:

  • 自动加载:把脚本放进 mpv 配置目录的scripts子目录,通常是~/.config/mpv/scripts/
  • 命令行指定:通过--script选项传入脚本路径;
  • 扩展名为.disable的条目总是被忽略;
  • 目录也可以代表一个脚本:mpv 会在其中加载main.js。若目录里同时存在main.luamain.js,只会加载其中一个,具体是哪个取决于 mpv 内部实现且可能随时变化;
  • 脚本名由去掉扩展名、把所有非字母数字字符替换为_得到(文档以my-tools.luamy_tools为例,JS 文件同理),运行时可用mp.get_script_name()取到。

写第一个 JS 脚本

直接采用官方文档的例子:播放器被暂停时退出全屏模式。把下面内容保存为一个.js文件(文件名可以任意,本文以~/.config/mpv/scripts/unfull.js为例;末尾额外加了一行print,方便加载时确认):

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

这段代码里有两个知识点:

  • mpmp.msg等模块不需要requirempmp.utilsmp.msgmp.optionsmp.input在 JS 环境中是预加载的,可直接使用——这正是与 Lua 的第一个差异。
  • 生命周期:脚本主体在启动时先执行一遍,随后 mpv 进入事件循环,你注册的观察者(如mp.observe_property)由事件循环驱动。lua.rst 特别提醒:脚本与播放器初始化并行启动,顶层执行时部分属性可能还没有有意义的值,因此不要在脚本顶层直接读这些属性,而应在mp.observe_property或事件处理器中读取。

随后像平时一样启动 mpv 即可(video.mp4替换成你要播放的媒体文件;也可用mpv --script <.js 文件路径> video.mp4显式加载该脚本):

mpv video.mp4

验证脚本真的运行了

  • 加载成功:终端打印unfull.js loadedprintmp.msg.info的别名;按 lua.rst 对日志级别的说明,默认情况下除vdebugtrace外的消息都会显示,所以info级别可见。
  • 行为符合文档示例:播放中进入全屏,然后暂停,播放器退出全屏。
  • 加载失败:控制台打印错误(前文的Can't load unknown script一类)。脚本加载失败不会阻止 mpv 继续启动,只是该脚本不生效。

脚本内部如果抛出 JavaScript 错误,可以用try { ... } catch(e) { ... }捕获;当错误是用Error(...)构造器创建时,e.stack提供堆栈跟踪,便于定位。

JS 与 Lua 环境的关键差异

除下述差异外,两侧 API 一致:javascript.rst "Scripting APIs - identical to Lua" 一节列出的mp.commandmp.commandvmp.command_nativemp.get_property/mp.set_property家族、mp.register_eventmp.observe_propertymp.add_key_bindingmp.add_hookmp.osd_messagemp.options.read_optionsmp.input.get等函数在 JS 中签名与语义相同,细节以 Lua 文档为准。

方面LuaJavaScript
模块加载需要require "mp.msg"五个模块预加载,无需任何 setup
错误表示返回nil,或value, error两个值返回undefined;错误原因经mp.last_error()获取(仅部分函数提供,文档中标注(LE)
一次性定时器mp.add_timeout(seconds, fn)id = setTimeout(fn, ms)(注意 Lua 用秒、JS 用毫秒)
周期定时器mp.add_periodic_timer(seconds, fn)id = setInterval(fn, ms)
JSONmp.utils.parse_json/mp.utils.format_jsonJSON.parse/JSON.stringify
语言LuaECMAScript 5

两条转换规则:

  1. 标准 JS API 优先setTimeoutJSON.stringify直接可用;反向地,mp.add_timeoutmp.utils.format_json在 JS 环境中不存在。
  2. 语言级别是 ES5,脚本后端是 MuJS(一个兼容的极简 ES5 解释器)。例如String.substring有实现,而常见但非标准的String.substr没有。写新语法前先确认 MuJS 的语言特性支持。

JS 环境还有几处 Lua 文档没有的机制:

  • dump:与print类似,但会递归展开对象和数组;
  • mp.last_error():在更新了 last error 的 API 调用后,返回空字符串表示成功、非空字符串表示失败原因;
  • 文件函数:mp.utils.read_file(fname [,max])mp.utils.write_file(fname, str)mp.utils.append_file(fname, str)。它们只接受文本内容,出错时抛出异常;write_file/append_file的路径必须以file://开头(防止参数写错的简单保护),例如mp.utils.write_file("file://~/abc.txt", "hello world")
  • CommonJSrequire(id)id总是按追加.js处理;以./../开头的 id 相对发起require的脚本解析,其余 id 先按绝对路径(如/x/y~/x)尝试,再按mp.module_paths数组顺序搜索全局模块。由于缺少fsprocess等 node.js 核心模块,大多数 node.js 模块无法运行——这套机制是为共享 mpv 脚本设计的,不是 node.js 的替代品;
  • init.js:mpv 为每个脚本初始化 JS 环境之后、加载脚本之前,会运行 mpv 配置目录根部的init.js,可用它统一更新所有脚本的环境(如mp.module_paths.push("/foo"))。注意:用--no-config启动时该文件被忽略;更新搜索路径要用push而不是整体重新赋值,否则会清掉已有的搜索路径。

边界与限制

  • mpv 中的 JavaScript没有标准库:与 mpv 之外的一切交互都局限在可用 API 上,通常经由mp.utils
  • 语言停留在 ES5;MuJS 未实现的标准特性不可用;
  • mp.utils.file_info(path)与 Lua 一致,展开~~/foo这类 meta 路径,其他 JS 文件函数会展开;
  • 定时器永远异步回调:setTimeout(fn)在返回之前绝不会调用fn,回调发生在本次事件循环迭代末尾或之后的迭代,因此setTimeout(fn)也可以当作一次性的 idle 观察者使用。

第一个脚本跑通后,API 细节可以继续查 Lua 文档(JS 复用同一套 API);想看printdump、定时器与require的默认实现,可以直接读 player/javascript/defaults.js。

【免费下载链接】mpv🎥 Command line media player项目地址: https://gitcode.com/GitHub_Trending/mp/mpv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 5:56:32

makefile完全指南:从目标依赖到自动化构建

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 5:56:30

730+免费API完整指南:快速找到合适的接口并跑通第一次调用

730免费API完整指南&#xff1a;快速找到合适的接口并跑通第一次调用 【免费下载链接】public-api-lists A curated list of free public APIs — searchable, community-maintained, with a free JSON API. 项目地址: https://gitcode.com/GitHub_Trending/pu/public-api-li…

作者头像 李华
网站建设 2026/9/11 5:55:37

时间戳排序并发控制:从原理到工程落地的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 5:46:15

CTF压缩包爆破全攻略:从原理到实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华