07 · 生态与演进
1. 版本与兼容
1.1 版本号
- 协议版本为
主.次.修订,宿主通过hg.env.protocol暴露(如"1.0.0")。 - 插件在
meta.protocol里只写主版本(整数)。
1.2 次版本只做增量
同一主版本内,新的次版本只能:
- 新增能力命名空间、新增可选方法
- 向
params增加可选字段,向返回类型增加可选字段 - 新增
hg.*模块或方法、新增可选特性 - 新增
meta的可选字段、新增错误码
由此得到双向的兼容规则:
| 一方 | 必须做到 |
|---|---|
| 插件 | 忽略 params 里不认识的字段;用 hg.env.features 与"方法是否存在"探测,而不是比较版本号 |
| 宿主 | 忽略 meta 与返回值里不认识的字段;插件未实现的可选方法按"不支持"降级;未知错误码按 INTERNAL 处理 |
增量扩展保证旧插件可继续在新宿主运行;新版插件在旧宿主上运行的前提是它为新增能力提供了有效降级。 “主版本相同”不代表宿主拥有插件需要的一切功能。
1.3 必需能力与降级
meta.features只作说明,不能代替运行时探测。可降级的功能用hg.env.features或方法存在性判断。- 无法降级的功能写入
meta.requires:feature对照hg.env.features,api对照宿主已实现的公开路径, 路径相对于 hg,例如html.parse、crypto.rsaEncrypt,不是插件方法,也不使用通配符。 - 宿主在启用前检查 requires,未知条目或缺少功能时保留安装文件但标记“不兼容”,展示缺少项,不执行工厂。 协议 1.0 的宿主必须实现 requires 检查,不得按普通未知元信息忽略已知 requires 内无法理解的需求。
- 1.0 核心 API 的存在不晚于类型定义;后续新增 API 可选,并提供 feature 或方法探测。 正式 1.0 不承诺兼容本草案的中间实现。
1.4 主版本
破坏性变更(删除或改变既有字段的语义)才升主版本。 宿主应当在升级主版本后继续支持上一个主版本的插件一段时间,并在插件列表里标注"面向旧版协议"。
2. 对标与取舍
参考了三个以插件为核心的开源项目(结论来自阅读其源码与文档):Cloudstream(视频,Kotlin 插件)、 Venera(漫画,JS 插件)、Miru(番剧 / 漫画 / 小说,JS 插件)。
| 设计点 | Cloudstream | Venera | Miru | HPP 的选择 |
|---|---|---|---|---|
| 插件单元 | 编译后的字节码包,需要 Gradle 工程构建 | 单个 .js,class X extends ComicSource |
单个 .js,注释头元信息 + export default class |
单个 .js,export const meta + 默认导出工厂函数 |
| 元信息读取 | 包内清单 + 仓库索引 | 正则找类名后执行,读实例字段 | 正则扫描整个文件里的 @key value |
结构化对象,可带设置项;不靠正则 |
| 批量索引 | 仓库文件指向若干插件列表文件 | 任意地址的 JSON 列表 | <仓库>/index.json |
manifest.json,支持相对路径与 URL |
| 更新 | 启动时比对版本并自动更新 | 比对索引版本后重新下载 | 无完整的更新路径 | 比对版本,来源地址或 updateUrl 取新文件 |
| 执行环境 | 与应用同进程、同权限,可调用应用的任何接口 | QuickJS,所有插件共用一个引擎 | 不同平台用不同引擎,各有一套桥接代码 | 统一引擎实现,每插件独立 runtime、上下文与调度,只能经 hg.* 与外界交互 |
| 超时 / 内存上限 | 协程超时(默认 120 秒,插件可调);无内存上限 | 无 | 无 | 有(02 §7) |
| 宿主如何调用插件 | 直接的方法调用 | 生成 JS 源码执行 | 把参数拼进源码字符串 | 参数走结构化的桥,不拼源码 |
| 网络 | 插件自带 HTTP 库直接联网 | 宿主 HTTP 封装 + 全局 fetch |
this.request,另可启用绕过封装的 fetch |
只有 hg.http,统一经宿主网络层 |
| 分页 | 页码 | 页码 + 最大页,或 next 令牌,两套方法 |
仅页码 | 统一的不透明游标 |
| 首页 | 插件声明若干分区,逐区分页,可标注横版图 | 探索页:分区页 / 列表页 / 混合 | 仅"最新"列表 | home 分区(可分页、可标注横版图)+ 独立的 feed |
| 播放解析 | 回调式地流出一批链接;按视频托管站拆出可复用的解析器 | 取章节与逐图加载配置分离 | 新版把"列出镜像"与"解析镜像"分离 | servers 列出播放线路,resolvePlayback(serverId) 按需解析;托管商解析器独立成插件,按主机模式路由 |
| 登录 | 数据源接口没有统一的登录流程 | 账号密码 / 网页 / Cookie 三种;约定抛字符串 Login expired |
网页登录后写 Cookie | 表单流(可多步)+ 可选网页视图;类型化的 AUTH_EXPIRED |
| 设置 | 插件自绘原生设置界面 | 插件内声明,含回调型按钮 | 运行时注册 | meta.settings 静态声明 |
| 界面能力 | 插件可直接操作原生界面 | 插件可弹消息、对话框、输入框 | 无 | 插件不绘制界面(见 §6 开放问题) |
| 聚合 | 搜索时并行查询各插件,按来源分组 | — | — | 同左,另有按标题与年份匹配的换源 |
| 作者支持 | 强类型语言 + 内置的冒烟测试工具 | 仅 JSDoc 桩文件 | 仅文档里的接口描述 | 计划提供类型定义、假宿主与内置自检 |
2.1 借鉴的做法
- 单文件插件 + 独立索引:Venera 与 Miru 都验证了这是社区最容易接受的形态。
- 插件驱动的首页分区(Cloudstream):插件声明若干分区,每个分区可独立翻页,并可标注用横版图。
对应
home。 - 把托管商解析独立成可复用的模块(Cloudstream 的 extractor):影视源大量使用同一批视频托管商, 逐源重写解析逻辑既浪费又容易失效。对应解析器插件(06 §3)。
- "列出可播放项"与"解析其中一项"分开:避免为了展示线路列表把每条线路都请求一遍, 也让按线路设置请求头变得自然。
- 稳定的条目标识(Cloudstream 为此单设了一个字段):页面地址可能带会过期的参数,
片库的键必须与之分开。对应 05 §1 对
id的稳定性要求。 - 分季、演职员、预告片(Cloudstream):通用查看器的详情页需要它们,均为可选字段。
- 聚合搜索按来源分组、按标题与年份匹配换源(Cloudstream):对应 06 §17。
- 粘贴链接直接打开(Venera 的
link):对应media.matchUrl与meta.linkDomains。 - 云端收藏夹分组(Venera):对应
library.listFolders。 - 逐条目的更新检查(Miru 的
checkUpdate):对应updates.check,改为批量以减少请求。 - 内置冒烟测试(Cloudstream)与应用内日志面板(Miru):对插件作者帮助最大的两件工具,列入路线图。
2.2 避开的坑
| 坑 | 出处 | HPP 的做法 |
|---|---|---|
| 插件与应用同进程同权限,出现过插件弹出广告跳转、拉起其他应用 | Cloudstream | 插件在独立引擎里,没有界面与系统接口,结构上做不到 |
| 索引里的类型字段是自由文本,出现拼写不一致的值 | Cloudstream | 枚举校验,不认识的值按 other 处理 |
| 接口版本号字段存在但不生效,不兼容要到运行时报错才暴露 | Cloudstream | 导入时检查 meta.protocol;运行时用特性探测 |
| 所有插件共用一个引擎与全局对象,一个死循环卡住全部,插件能读到彼此的数据 | Venera | 每插件独立 runtime、执行调度与存储 |
| 没有调用超时与内存上限 | Venera、Miru | 截止时间、内存上限、崩溃后停用 |
| 把调用参数拼进 JS 源码,搜索词里的引号即可破坏调用 | Miru | 参数经结构化的桥传递 |
| 不同平台使用不同引擎,维护两套桥接,个别平台不可用 | Miru | 单一可嵌入引擎 |
| 用正则在全文件里取元信息;必填字段缺失时直接崩溃 | Miru | 结构化 meta;缺字段给出明确的导入错误 |
| 监视插件目录自动重载,出现插件"丢失""显示未安装" | Miru | 导入、更新、移除都是显式操作 |
| 没有更新路径 | Miru | 版本比对 + 明确的更新地址 |
| 文档与实现的字段名不一致 | Venera | 类型定义文件作为唯一事实来源,文档由它对照 |
| 约定抛特定字符串表示登录失效 | Venera | 类型化错误码 |
3. 客户端实现基线
HGhub 0.0.1 是保留既有前端风格的新插件视频播放器,不延续旧平台业务,也不迁移旧账号与片库。
src-tauri/crates/hpp提供独立运行时、通用网络、校验、插件管理、媒体会话和本地片库。src-tauri/src仅保留 Tauri 适配、应用设置、日志与窗口能力。- 界面通过通用宿主命令调用插件,不含站点接口、域名、凭据或解密知识。
- 本地片库从空库开始,条目键为“插件 ID + 条目 ID”;分集进度另含播放列表 ID 和真实分集 ID。
- 既有排行榜与平台账号入口不保留;榜单由 home 分区或 catalog 排序表达,账号归属插件。
4. 实现要点(非规范)
- 薄桥 + JS 前导脚本:原生侧只暴露一个结构化的消息入口,
hg.*的大部分便利封装用一段随宿主 发布的 JS 前导脚本实现。原生面小,接口演进主要改脚本。 - 读取
meta:在一个用完即弃、无外部 I/O 的独立运行时里限时求值模块,只取meta导出, 不调用默认导出;不能把“未执行工厂”等同于“没有执行顶层 JS”。 - 参数传递:宿主调用插件方法时,参数以结构化值传入,不生成 JS 源码。
- 插件目录:每个插件一个文件
<id>.js,旁边是它的存储与设置;宿主另存一份索引记录来源与导入时间。 - 媒体会话:完整传输规则见 09。资源登记必须保留提供者、代码修订与解析链, 不能仅保存 URL 和请求头。平台可选择不同本地传输实现,公开的插件接口保持一致。
- 作者工具:
- 插件详情页的日志面板与网络请求记录(地址、状态码、耗时、是否命中缓存)。
- "从文件重新加载":作者改完本地文件后一键重载,不必重新导入。
- 插件自检:按实际能力选择点播、直播或解析器路径;没有内容入口时让用户提供条目 ID、链接或播放页, 不伪造 query,也不承诺任意插件都能自动跑完。步骤可跳过,区分缺少输入、不支持与真正失败。
- 网络请求面板:地址、状态码、耗时、是否命中缓存。
- 类型定义文件,以及一个能在 Node.js 里运行的假
hg,用于单元测试。
5. 路线图
| 阶段 | 内容 | 完成标志 |
|---|---|---|
| P0 | 协议类型与校验、假 hg、虚构 JSON/HTML 源及依赖 Cookie 的源与解析器夹具;独立 runtime、取消与预算;hg.http/storage/cookies/log/crypto/codec/html;本地导入、初始化与 endpoints;media/search/resolver;媒体会话、HLS 与 progressive |
虚构链路跑通导入 → 能力/线路初始化 → 搜索 → 详情 → 播放 → 刷新;验证取消、MP4 拖动、HLS 密钥与解析器资源归属;五端验证核心传输,完成后冻结核心 1.0 契约 |
| P1 | 全新的插件片库;feed/catalog/comments/interact/auth/ui、封面变换;插件页、基础日志、自检;移除宿主站点知识 |
点播功能经外部插件使用,多来源主键已就绪;公开树与发布产物无真实上游知识 |
| P2 | manifest.json 与任意 URL 导入、更新检查、订阅;home 与综合编排;分类分栏、搜索范围与聚合展示;移除独立排行榜 |
同时启用多个插件,失败互不拖累;同版本覆盖、升级、降级及播放中延后自动更新可用 |
| P3 | 换源与来源关联、季集匹配;articles/library/progress/updates;网络请求面板与完善的作者文档 |
换源保留旧来源与进度,不错配季集;作者可依据类型和虚构样例独立实现插件 |
| P4 | live、纯直播源路径;可选 hg.webview;声明支持的 DASH 与分片变换;跨端能力验收 |
频道播放/刷新、网页登录和所声明可选特性通过目标平台验收,完成 1.0 能力实现 |
这些阶段是实现顺序,不表示可以把未实现模块对外声称为已支持。冻结后发布为完整 1.0 的宿主必须满足所有必需契约; P0 至 P3 的中间构建标注为开发版。可选特性仅在实际实现并通过验收后声明。
5.1 P0 验证资产
虚构示例只连接本地测试夹具或保留示例域名,使用虚构 ID、密钥和响应,不提供任何真实影视来源。 测试夹具通过测试配置放宽本地地址策略,不改变生产默认网络规则。协议示例不是面向用户分发的内容插件。
至少覆盖:JSON 点播源、HTML 点播源、需 Cookie 的数据源 → 独立解析器、首次 endpoints 无选择、 同插件交错调用与取消、运行时超额重建、分页恢复、HLS 子资源归属、Range、有效期刷新与插件更新使旧会话结束。 类型定义与返回值校验同步维护;类型能编译不代替实际媒体链路验收。
6. 候选扩展与开放问题
6.1 1.0 之后的候选
| 方向 | 说明 |
|---|---|
| 网页视图提链 | 宿主在后台加载播放页并截获其中的媒体请求,把地址交还插件。Cloudstream 以此应对难以还原的页面脚本;五端的实现成本高,需单独评估 |
| 一键导入链接 | 注册应用链接(如 hghub://import?url=…),网页上点一下即可导入插件或插件列表 |
| 体验增强类数据源 | 弹幕、字幕库、影视资料、观看记录同步 |
| 阅读类内容 | 漫画、小说;需要新增一组能力,文件格式与运行时不变 |
| 下载 | 1.0 不要求下载界面;后续复用媒体会话、解析链和刷新语义,不假定一个静态 URL 足够完成长时间下载 |
6.2 开放问题
| 问题 | 现状 | 备选 |
|---|---|---|
| 逐请求的媒体钩子 | 只有静态请求头与字节变换 | 允许插件对每个分片请求动态改写地址或请求头(Cloudstream 有此接口);代价是播放期间高频唤醒插件 |
| 独立音轨 | 依赖 HLS / DASH 清单自带的音轨 | 在 PlaybackInfo 里增加外挂音轨列表 |
| 调用中途的用户输入 | 不支持:插件在方法执行期间不能弹窗提问 | 用插件页的表单(ui)或网页视图;若确实需要,可让 INTERACTION_REQUIRED 携带一个表单 |
| 令牌的存放 | 插件级明文存储,认证范围由插件决定 | 系统安全存储作为后续可选能力,不作为重构前置要求 |
| 限速默认值 | API 默认 5 次/秒,突发 15;媒体独立排队 | 根据真实运行调优宿主策略,不改变协议固定数据契约 |