大家好,我是 FluxDown 的作者。 FluxDown 是一个开源( AGPL-3.0 )的多协议下载器,引擎用 Rust 写,支持 HTTP / BT / HLS / DASH / FTP / eD2K ,有桌面端、NAS 版(带 Web 界面)和原生 Android / iOS 。
十月份主要的工作是 插件系统 ,已经随 v0.6.0 发布。下面讲讲它能做什么,以及我在安全方面是怎么设计的。
插件能做什么 一个插件就是一个文件夹(打包后是 .fxplug ),里面放一份 manifest.json 和几个 JS 脚本。目前支持这些能力: 能力 用途举例 resolver 把分享页链接解析成直链,例如网盘、视频站 hooks 下载完成后自动调用 ffmpeg / yt-dlp 做后处理 interceptors 在下载的各个阶段改写请求头、Cookie 、分片、凭据,并决定失败后是否重试 auth 扫码 / 验证码登录站点,之后复用登录态 订阅源 在内置 RSS 之外提供新的订阅来源,例如 UP 主投稿 界面 新增页面、设置页签、任务详情页签、命令面板命令、任务右键菜单 浏览器嗅探规则 插件给浏览器扩展下发嗅探规则 页面脚本 插件经浏览器扩展在指定网站上运行 JS ,并能与插件本体通信 插件市场里已经有社区作者写的插件,比如夸克网盘分享转存、B 站视频下载(番剧、投稿订阅、扫码登录)、M3U8 解析、GitHub 下载加速: 一份界面代码,桌面端和 Web 端都能显示 插件界面用 JSX 编写,界面数据交给宿主渲染。
同一份代码可以在 GPUI 桌面端和 NAS 的 Web 界面中显示。下图是示例插件在 Web 端的面板:插件通过 flux.tasks 创建任务,表格里能看到实时进度和状态。
内置的组件有:标题、徽标、进度条、callout 、表格、表单、Markdown 、代码块等,也支持定时器和事件订阅。有特殊需求的插件,还可以写 桌面原生界面 ( GPUI 脚本),但这需要单独申请 desktop 权限。
安全:插件默认能做的事情很少 下载器插件要接触 Cookie 、网络和文件,所以安全是我花时间最多的部分: 沙箱 :脚本运行在 QuickJS 里,基础能力只有 flux.* ( HTTP 、键值存储、临时文件、日志),不能创建任务,也不能读写任意文件。权限需要申请 :创建任务、拦截下载、登录、浏览器规则、页面脚本、桌面界面、后台常驻都属于单独的权限。
请求了权限的插件,装上后保持停用,用户逐项看过并授权后才会启用。插件更新后如果新增了权限,会再次询问。
网络边界 :沙箱里的 flux.fetch 默认只能访问公网地址(只有开发模式下的插件才允许访问内网,方便调试);桌面原生脚本只能连接授权时列出的主机。风险分级 :授权对话框会显示「标准 / 较高权限 / 完全信任」三档,带「完全信任」的插件必须勾选「我已了解」才能授权。
审计日志 :授权、安装、网络请求、创建任务、写文件等操作都有记录,可以导出为 JSON 。熔断 :resolver 、拦截器等连续 3 次超时或超出内存限制,插件会被自动禁用。
解析失败直接报错( fail-closed ) :解析失败时任务进入错误状态, 不会 退回去下载原始页面,避免把一个 HTML 页面当成视频保存下来。页面脚本符合浏览器商店政策 :页面脚本只通过浏览器的 userScripts API 执行,扩展里不使用 eval ,也不加载远程 。
每个插件第一次启用页面脚本时,需要用户在扩展选项页确认要匹配的站点。写一个插件 SDK 已经发布到 npm: npx fluxdown-plugin-sdk create my-plugin --identity me@my-plugin --name "My Plugin" cd my-plugin && npm install npm run dev # 保存即重新构建 npm run pack # 打包成 .fxplug 开发时,在「设置 → 扩展 → 插件 → 从目录安装」中填入 dist/ 目录。
改了 resolver 、hook 不用重启;改了 manifest 后点一下「重新加载」即可。 SDK 里带的 manifest 校验器,和应用、插件索引仓用的是 同一套规则 (跨实现共用测试夹具),本地校验通过,就不会在审核时才发现问题。
最简单的 resolver 大概长这样: // manifest.json: "resolvers": [{ "match": { "urls": ["*://example-files.com/share/*"] }, "entry": "resolver.js" }] async function resolve(ctx) { const id = ctx.url.split("/share/")[1]; if (! id) return null; // 放行,按原 URL 下载 const res = await flux.fetch({ url: "/ai-market-guide/ + id, headers: { Referer: ctx.url }, }); if (res.status !
== 200) throw new Error("API returned " + res.status); const data = JSON.parse(res.body); return { url: data.directUrl, fileName: data.name }; } 文档(中英双语)写得比较全,从「写第一个插件」到拦截器、页面脚本、桌面界面都有: 链接 官网 / 下载: /ai-market-guide/ GitHub: /ai-market-guide/ 插件开发文档: /ai-market-guide/ 插件索引仓(提交插件): /ai-market-guide/ 欢迎来写插件,也欢迎提需求:你们最希望下载器支持哪个站点?有人感兴趣的话,我可以先写一篇完整的插件开发教程。