mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-05-06 20:02:57 +08:00
插件系统(v1,实验性)
Warning
插件是进程内动态库(
dll/so/dylib),与宿主进程同权限运行
没有沙箱、权限隔离、签名校验,也没有网络或文件系统限制
插件还可以读取完整的宿主配置(包括sessdata)
当前示例
- 当前仓库的示例插件只有一个:
basic-example - 位置:
src-plugin/examples/basic-example - 用途:演示 Descriptor、Hook 处理、异步逻辑、读取宿主配置、返回修改后的
payload
快速构建示例
cd src-plugin/examples
cargo build --release
构建产物位于 src-plugin/examples/target/release/,文件名按平台分别是:
- Windows:
basic_example.dll - Linux:
libbasic_example.so - macOS:
libbasic_example.dylib
如何加载插件
- 在应用设置页的插件面板点击“添加插件”,选择动态库文件
- 后端要求路径必须是绝对路径,且文件必须存在
- 成功后会写入
app_data_dir/plugin.json持久化配置 plugin.json保存字段:path、enabled、priority、descriptor
Descriptor(插件内声明)
PluginDescriptorV1 由插件代码返回,宿主不会直接修改:
sdk_api_version:SDK API 版本,当前必须等于1id:插件 IDname:展示名称version:插件版本hooks:声明插件希望被调用的 Hook 点列表failure_policy:失败策略,FailOpen或FailCloseddescription:插件描述
运行顺序与优先级
- Hook 点相同的插件,宿主按
priority从大到小执行 - 每个 Hook 点按顺序串行执行,不是并行
- 前一个插件返回后的修改,会成为后一个插件看到的输入
Hook 时机(以实际代码为准)
| HookPoint | 触发位置 |
|---|---|
AfterPrepare |
prepare() 成功之后,开始下载前 |
BeforeVideoProcess |
视频任务和音频任务结束后,视频处理任务前 |
OnCompleted |
所有任务结束后,completed_ts 已写入后 |
三个 Hook 都可读写 progress,但修改只有在当前 Hook 返回后才会被宿主应用。
输入输出协议
- 输入:
HookInputV1 { hook_point, payload, readonly_meta } - 输出:
HookOutputV1 { payload } payload是枚举HookPayloadV1,必须与hook_point匹配- 不匹配会被判定为插件输出无效,再按失败策略处理
readonly_meta包含app_version、os、arch、process_id
可修改范围与约束
payload.progress大多数字段都可被插件修改task_id明确禁止修改,改动会被宿主拒绝- 宿主没有提供修改Config的 Host API
失败策略
FailOpen:插件出错时记录日志,继续执行后续流程FailClosed:插件出错时中断当前下载流程并返回错误- 插件返回
Err(...)时,宿主会调用bilibili_video_downloader_plugin_last_error_v1读取错误文本
Host API(当前仅 v1)
插件可在 on_hook 中调用:
host::get_config():读取宿主配置快照(HostConfigV1)
注意:
- 该配置是只读快照
- 返回内容包含敏感信息(例如
sessdata)
SDK 入口(插件侧)
- 实现 trait:
PluginV1 - 使用宏导出:
export_plugin_v1!(YourPluginType) - 插件类型需要满足:
Default + Send + 'static
常规最小结构:
use bilibili_video_downloader_plugin_sdk::{
HookInputV1, HookOutputV1, PluginDescriptorV1, PluginV1, export_plugin_v1, eyre
};
#[derive(Default)]
struct MyPlugin;
impl PluginV1 for MyPlugin {
fn descriptor(&self) -> PluginDescriptorV1 {
unimplemented!()
}
fn on_hook(&mut self, input: HookInputV1) -> eyre::Result<HookOutputV1> {
let _ = input;
unimplemented!()
}
}
export_plugin_v1!(MyPlugin);