爱意满满的作品展示区。
clow

给开源下载器 FluxDown 做了一套插件系统:网页解析、拦截下载、自定义界面,桌面和 Web 端通用

  •  1
     
  •   clow · 3 days ago · 681 views

    大家好,我是 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,也不加载远程 <script>。每个插件第一次启用页面脚本时,需要用户在扩展选项页确认要匹配的站点。

    写一个插件

    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: "https://example-files.com/api/file/" + 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 };
    }
    

    文档(中英双语)写得比较全,从「写第一个插件」到拦截器、页面脚本、桌面界面都有:

    文档

    链接

    欢迎来写插件,也欢迎提需求:你们最希望下载器支持哪个站点?有人感兴趣的话,我可以先写一篇完整的插件开发教程。

    4 replies  •  2026-10-22 18:10:02 +08:00
    shakukansp
        1
    shakukansp  
       3 days ago
    看下 issue822
    enderftt
        2
    enderftt  
       3 days ago
    安卓 app 上似乎没看到插件
    clow
        3
    clow  
    OP
       3 days ago
    @shakukansp 明天我测试下,尽快修复
    clow
        4
    clow  
    OP
       3 days ago
    @enderftt 目前插件的能力太多,移动端还不行,等后面看看,如果可以可能会实现部分功能
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1260 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 93ms · UTC 20:43 · PVG 04:43 · LAX 13:43 · JFK 16:43
    ♥ Do have faith in what you're doing.