频道 插件
版本v0.4.1.0 API1.0.0 测试199 通过

插件

项目自带两个插件;事实上其它东西也都是插件 —— Kotlin 加载器、HTTP 后端、HLS 适配器。内核本身不认识任何协议。

官方插件

HLS VOD Backend

把 `.m3u8` 当作要拼装的流而不是要保存的文本文件:解析清单、选码率、分片并发(分片级重试)、AES-128 解密、字节范围。

dev.turbodl:turbo-plugin-hls:0.4.1.0

backend.hlsturbodl-protocoldev.turbodl.cap.hlsdev.turbodl.cap.m3u8

JavaScript Plugin Loader

加载用 JavaScript 写的 TurboDL 插件。每个脚本一个 QuickJS runtime 与 context、一套稳定的 `host`/`plugin` ABI,以及一套「绝不在脚本还在执行时关掉 runtime」的加载/卸载流程。

dev.turbodl:turbo-plugin-js:0.4.1.0

loader.jsturbodl-loaderdev.turbodl.cap.jsdev.turbodl.cap.quickjs

Platform Link Parser

把分享链接变成可下载请求:微博(最高可用清晰度 MP4 + 图集)、小红书(无水印视频 + 原图)、Pixiv(每页 `urls.original`)。注册一个 `LinkParser` 与每个平台一条 `ProtocolClaim` — 引擎对这三个站点一无所知。

dev.turbodl:turbo-plugin-parser:0.4.1.0

parser.platformsturbodl-parserdev.turbodl.cap.parser.platforms

能力,以及被拒绝的东西

两个官方插件一共声明了 5 项能力。它们不做的事情都列在旁边 —— 当另一种做法是产出一个损坏的文件时,「拒绝」本身就是功能。

声明的能力

turbodl-protocol · backend.hls2 项能力 · hls
turbodl-loader · loader.js2 项能力 · 不声明协议
turbodl-parser · parser.platforms1 项能力 · weibo, xhs, pixiv

从各插件的 turbodl-plugin.json 里读出来的。

明确拒绝

HLS VOD Backend直播清单(没有 `EXT-X-ENDLIST`) · DRM / `SAMPLE-AES` · fMP4 `EXT-X-MAP` · 不连续标记
JavaScript Plugin Loader注册 `DownloadBackend`(字节面留在 Kotlin 一侧) · 发布服务(服务是 Kotlin 对象图) · `require` / npm / `fetch` —— 宿主 ABI 就是全部可用面 · `jar:` 与远程脚本源(目前只支持本地文件 / `inline:` / `data:`)
Platform Link Parser登录 — 需要会话的平台返回 null,由宿主自己的账号处理接手 · Pixiv 动图(ugoira):zip + 帧时长表需要图像处理 · Pixiv 小说 · 需要签名的接口(抖音 / 快手 / B站):各自的签名算法仍留在宿主侧

不支持的流会响亮地失败,而不是产出一个看起来正常的文件。

插件分类

插件在清单里声明一个分类。分类只说明它能接到哪里,不说明别的。

turbodl-plugin.json · category

turbodl-backend协议后端(替换或扩展内置 HTTP 引擎)。
turbodl-protocol声明它处理哪些协议 / scheme(清单的 `protocols` 列表)。
turbodl-adapter对接外部下载器。
turbodl-parser把分享链接 / 磁力 / 口令变成下载请求。
turbodl-hook观察或改写请求与结果。
turbodl-loader加载用别的语言写的插件 —— JS 加载器就属于这一类。

分类就是这个运行时的全部授权依据:它决定插件可以注册哪些扩展点。

写一个自己的插件

插件就是一个实现了某个接口的普通类。第三方插件通过 GitHub topic + 清单被发现 —— 没有需要注册的中心仓库。

Kotlin
class MyPlugin : Plugin {
    override val id = "parser.example"

    override fun onLoad(context: PluginContext) {
        context.registerExtension(ExtensionPoints.LINK_PARSER, LinkParser { input ->
            input.takeIf { it.startsWith("example://") }
                ?.let { listOf(DownloadRequest(url = it, destination = File("out.bin"))) }
        })
    }
}

turbodl-plugin.json

{
  "manifestVersion": "1.0",
  "id": "parser.example",
  "name": "Example parser",
  "description": "Turns example:// links into download requests.",
  "version": "0.1.0",
  "author": "you",
  "homepage": "https://github.com/you/turbodl-plugin-example",
  "license": "MIT",
  "category": "turbodl-parser",
  "capabilities": [
    "dev.turbodl.cap.parser"
  ],
  "protocols": [
    "example"
  ],
  "turbodl": {
    "apiMajor": 1,
    "requiredApiVersion": "1.0.0"
  },
  "entry": {
    "language": "kotlin",
    "pluginClass": "com.example.ExamplePlugin"
  },
  "extensionPoints": [
    "turbo.linkParser"
  ]
}

六个必填字段、一个分类、一个入口。参考页列了每个字段与允许的取值。

关于安全,说清楚