插件
项目提供三个官方插件,另有运行时与基础装配模块。核心本身不认识任何具体协议。
官方插件
backend.hls
查看
HLS VOD Backend
把 `.m3u8` 当作要拼装的流而不是要保存的文本文件:解析清单、选码率、分片并发(分片级重试)、AES-128 解密、字节范围。
- 分类
- turbodl-protocol
- 协议
- hls
- Artifact
- turbo-plugin-hls
loader.js
查看
JavaScript Plugin Loader
加载用 JavaScript 写的 TurboDL 插件。每个脚本一个 QuickJS runtime 与 context、一套稳定的 `host`/`plugin` ABI,以及一套「绝不在脚本还在执行时关掉 runtime」的加载/卸载流程。
- 分类
- turbodl-loader
- 协议
- 无
- Artifact
- turbo-plugin-js
parser.platforms
查看
Platform Link Parser
把分享链接变成可下载请求:微博(最高可用清晰度 MP4 + 图集)、小红书(无水印视频 + 原图)、Pixiv(每页 `urls.original`)。注册一个 `LinkParser` 与每个平台一条 `ProtocolClaim` — 引擎对这三个站点一无所知。
- 分类
- turbodl-parser
- 协议
- weibo, xhs, pixiv
- Artifact
- turbo-plugin-parser
能力,以及被拒绝的东西
三个官方插件一共声明了 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 + 清单被发现 —— 没有需要注册的中心仓库。
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"
]
}六个必填字段、一个分类、一个入口。参考页列了每个字段与允许的取值。