HGhub 开发者HPP 1.0 草案

实现进度与验证范围

应用版本 0.0.1,当前为 插件分发/浏览/点播/直播/图文/本地片库/账号表单/评论互动开发版,运行时协议报告 1.0.0-dev。协议草案描述完整目标; 本页记录实际代码范围,不能把 TypeScript 中存在的接口视为当前客户端已经支持。 本阶段以 Windows 为主,Android 已验证单文件导入与单部真实 MP4 播放;未完成五端验收,尚未冻结 HPP 1.0。支持范围与缺口见下文。

这是保留既有 HGhub 前端风格的全新插件播放器。不读取、不迁移旧账号、收藏或历史; 应用使用独立标识 com.hghub.player,首次启动空片库。旧站点 API、线路、登录与媒体解密代码已移除。

已落地

层 实现
独立宿主 src-tauri/crates/hpp,不依赖原短剧平台 API、凭据、媒体解密逻辑
插件管理 原生多文件选择、粘贴、URL 和本地/远程 manifest 勾选导入;简介与来源列表名称展示;元信息及版本校验,降级显式选择,启停、重试、移除、索引恢复
插件更新 手动检查和勾选安装;updateUrl 优先、数值版本比较、来源记录持久化;预览不替换现有插件,修订变化拒绝过期更新;失败后恢复上一可用文件,保留设置与存储
初始化 工厂与能力发现;首次以空 endpoint 获取线路,选定默认线路后重建;设置和站点线路变更重建
执行 每插件独立 QuickJS runtime、线程、64 MiB 堆;8 个并发调用、40 个总排队槽位、调用预算与同步中断
生命周期 Promise/await 上下文归属、调用取消、遗漏 signal 的 HTTP 取消、定时器清理、空闲卸载、连续故障停止及显式重试
数据桥 全量带标签的可传递值;支持 Uint8Array,不与普通对象冲突;深度、字节预算与结果校验
持久化 插件 ID 隔离的键值存储,二进制、TTL、原子落盘、容量上限;Cookie 独立罐与 domain/path 匹配
HTTP DNS/重定向检查,复用应用现有网络策略,取消、请求/响应上限、字符集、限速、Retry-After 冷却;GET/HEAD 重试和有界匿名 GET 响应缓存
原生工具 URL、URLSearchParams、编解码、摘要/HMAC、AES CBC/ECB/CTR/GCM、随机数,原生 HTML/CSS 选择器
媒体 随机端口 loopback HTTP 服务,progressive 流式代理与真实 Range/HEAD/206/416,HLS 主/子清单及 URI 属性改写
资源归属 解析器匹配/选择;解析器独立 Cookie、请求头和变换;源与解析器字幕保留各自提供者;同一解析动作共享预算
字节变换 有界图片/清单/分片/密钥的原生 AES-CBC、plugin transformResource;对输出计算局部范围
会话 图片批次和播放会话;释放取消传输,依赖插件停用/移除/更新/设置变更时释放;修订校验防止旧会话使用新环境
Windows 页面 经典沉浸播放器外壳;首页直接播放,片源范围在设置选择;推荐列表保留来源/分区排序与显隐;分区完整列表导航;分类树成功结果按插件修订缓存、筛选与排序;推荐/分类/搜索/分区滚动分页、失败保留内容并重试原游标、ID 去重与重复游标终止;单源/多源/综合搜索、来源选择;插件管理、设置、线路、日志
站点线路 手动刷新、可达性探测与切换;刷新后保留仍存在的线路,否则选择第一项并重建;探测不携带插件默认头或 Cookie,不自动切换
本地片库 Rust 原子落盘,收藏/追更/历史按插件与作品隔离;进度另含播放列表与真实分集 ID;离开/暂停/结束保存、重新选集续播;插件移除后记录保留
详情与播放 按季分组,显式 season/episodeNumber 唯一时定位候选,不用 index 猜测;分集分页、刷新、HLS/普通视频播放;同版本覆盖修订使页面与会话失效
跨来源换源 详情页与播放器共用换源入口;并行搜索其他可用来源,标题/年份/类型排序、独立失败重试;候选详情、分集分页与用户确认;显式季集唯一匹配才预选,沿用旧秒数默认关闭,新来源进度优先;来源关联原子保存,“我的”可查看关联来源,旧收藏/追更/历史独立保留
直播 纯直播源可只实现 live;首页入口显示频道分组、频道分页、频道图标、节目单按需加载;直播解析复用 HLS/普通流、线路/流选择和媒体会话,调用只传 channelId
沉浸式信息流 分类页按具体方法显示 feed 入口;首页共用沉浸播放;单条竖屏播放、上下条按钮与方向键、游标分页及 ID 去重;直接使用 entryEpisodeId,缺失时取详情及必要的首批分集;本地收藏、评论与云端互动;按真实分集保存进度,自动下一条遵循偏好;覆盖页面或离开后释放播放
图文 articles.list/getDetail 独立列表与详情;正文按允许标签重建、图片走所属插件媒体会话、外链使用外部浏览器;支持文内视频、关联点播作品、评论与互动
我的 插件账号资料、云端收藏/点赞/历史分页与本页导入本地;手动批量追更检查每批 ≤50,忽略非本批条目
云端目录与关注 云端收藏目录按原始 folderId 请求,关注列表独立游标分页、头像走所属插件媒体代理;仅实现 interact.follow 时显示取消关注,成功后移除对应作者
评论与回复 详情页按需打开作品/当前真实分集评论,回复使用 replies/commentId;独立分页与 ID 去重、纯文本、头像代理、评论点赞;发送返回评论时插入,无返回则刷新;失败保留草稿,离开页面取消未完成调用;不支持的目标/操作独立隐藏
云端互动 详情页按具体方法显示云端点赞/收藏/作者关注,成功后更新状态;本地收藏单独保留;认证失效清除互动状态并沿用所属插件的登录引导
上游进度回报 实际 playing/pause/ended 产生 start/pause/end;单次播放消费者按单调时钟每 30 秒至多一个 heartbeat;刷新/切流不重复 start,带真实 episodeId 和有限秒数;失败静默,认证错误只清除账号缓存不弹出播放提示
插件表单 auth/ui 必需方法检查、返回状态/字段/资料校验;插件设置与日志面板内展示登录、账号、退出与自定义操作;八种字段、多步值合并、隐藏挑战更新、必填校验、验证码/头像媒体代理
登录状态 插件自行持久化会话;宿主输入仅在当前表单内存中保留;AUTH_INVALID 保留填写值,AUTH_REQUIRED/EXPIRED 清除页面账号缓存并引导插件登录;退出失败后重新查询插件资料,不清空插件存储
播放恢复 页面按到期时间/致命播放错误尝试一轮重新解析,携带 reason:refresh,保留当前播放位置;支持手动刷新
播放选择 源/解析器各自的候选流和播放线路控件;按阶段与插件 ID 保存原始 serverId;手动切换保留当前秒数,刷新保留选择;解码失败刷新一次后按设置有限切换候选流
解析器站点偏好 播放页主机由宿主提取;点播与共用播放器提供解析器选择及默认关闭的“记住”;成功选择后原子保存,跨站点不复用手动选择;流指定解析器优先,停用/移除/不匹配的记忆不参与自动路由;设置可逐项忘记或清空
开发资料 完整草案类型 src/plugins/protocol.ts;可注入传输/加密/HTML 适配器的内存假宿主 src/plugins/mockHost.ts;纯虚构源和真实宿主 stdio 测试适配器

当前发现并调度的方法为:

当前启用检查要求 source 实现 media 或 live 的全部必需方法,resolver 实现 resolver.resolve。 纯直播源不得声明返回点播 MediaItem 的 home/catalog/search/feed/library 能力而不实现 media。 声明 auth 时必须实现全部四个方法,声明 ui 时必须实现两个方法;不完整命名空间初始化失败。 上述方法已接入点播详情、播放器、信息流和“我的”的对应用户流程。 未实现的可选特性仍未宣称支持。

表单管理复用 hpp_manage IPC:form.actions 的 target 为 auth 或 ui; form.submit 的 target 为 { kind: "auth" | "ui", id },values 为字符串键值对象, 两者都使用界面预生成的 requestId,进入与普通能力相同的取消/截止时间作用域。 表单内单个非法字段使该表单无效;动作/登录方式列表中的坏条目独立丢弃。 校验日志只含字段路径和原因,不含输入值。网页视图仍未实现,未声明 webview 特性; 无 form 的登录方式可直接提交,但不能据此认为开发版已支持网页登录。 播放解析链上的认证错误由宿主标注实际提供者,界面据此进入数据源或解析器的账号区块; 不会信任插件 details 中自报的其他插件身份,也不把解析器登录态与源登录态混用。

仍需完成

  1. 分发平台完善:订阅、启动自动检查、自动更新偏好、24 小时冷却及播放期间延后安装已实现并有 Rust 回归。Android content URI 单文件读取已修复实测;本地列表相对兄弟文件仍需目录授权。
  2. 其他能力:云端收藏目前使用上游默认目录;详情互动中的目录选择、分集点赞和更完整账号状态联动后续完善。信息流复用经典播放器的手势与控制器,仍需更多设备验收。
  3. 播放完善:设备能力与默认清晰度偏好、过期候选索引重新定位、服务器失败切换;完整字幕格式处理。当前候选流有限切换只处理播放器致命错误,解析阶段的认证/锁定错误停止并引导用户处理。
  4. 宿主刷新调度:下载和后台消费者共享的 single-flight 刷新、资源映射原子替换、可恢复错误分类;当前自动刷新由播放页执行。
  5. 媒体预算与缓存:播放/图片/下载分队列,按主机共享并发,资源缓存与淘汰,长时间直播及低延迟 HLS 验证。
  6. 字节区间变换:带字节变换的 HLS BYTERANGE 当前明确返回 UNSUPPORTED;不对截断密文直接解密。
  7. API 与可选功能:RSA 是协议必需 API,目前缺失,不能归为可选;网页交互、DASH、下载为可选且未实现。静态 hg.env.media 尚未由实际播放路径探测。
  8. 应用功能:插件自检、数据清理和占用、应用更新。此项目不开发旧平台数据迁移。
  9. 平台验收:Windows 原生窗口和五端完整媒体回归仍未完成;Android 已定位并修复文档选择器 URI 导入失败,粘贴与文件导入新 APK 实测成功。

缓存当前只接纳显式 credentials:"omit" 且无 Authorization/Cookie 的 GET; 对默认 include 但当时恰无 Cookie 的请求采取保守的缓存未命中,不改变响应语义。 假宿主不模拟 QuickJS 隔离、完整配额或异步调用上下文;这些必须使用 Rust 宿主测试。

分发请求使用独立、匿名的宿主下载环境;远程列表重定向后按最终 URL 解析相对路径, 只下载勾选条目,文件身份与列表不一致时仅该条失败。下载、检查和等待安装锁期间支持取消; 文件替换已开始的单个安装完成后再停止后续条目,不将取消解释为回滚上游操作或存储写入。 离开页面后返回的预览会立即释放。原平台专用调试脚本仅保留在被忽略的本地目录, 公共测试入口只依赖虚构源,不依赖私有插件。

播放选择的可执行契约

范围与触发

对应 05 §7、06 §2/3、08 §7 与 09 §2/5。此处记录宿主 IPC,插件的 PlaybackParams 不增加解析器字段。

签名

hpp_manage("media.prepare", { id, requestId, args, streamIndex?, resolverId?, resolverStreamIndex?, resolverServers? })

请求、响应与环境

校验与错误

条件 结果
索引为负数、字符串或大于 15 INVALID_ARGUMENT,不执行插件
索引超出本次候选列表 明确报错,保留手动选择控件
流声明 resolverId,同时存在旧 UI 选择 按流声明路由;声明的解析器不可用则明确报错
resolverServers 包含其他解析器条目 忽略非当前提供者条目,不转交给源
源/解析器返回非法线路条目 独立丢弃;重复 ID 保留首条;移除未知字段
AUTH_REQUIRED、AUTH_EXPIRED、CONTENT_LOCKED 停止解析,不自动轮询候选

正常、基线与异常案例

回归断言

Rust tests/playback_selection.rs 检查两阶段参数归属、指定解析器优先、解析信息合并、选择信息不泄露资源配置、非法索引和坏条目处理。 Edge 集成测试检查真实流/线路切换保留秒数、两阶段选择刷新后保留、跨集与重载持久化、一次刷新与有限候选重试、首选格式不支持后手动播放备用候选。

错误与正确示例

错误:把解析器线路放入 args.serverId,导致源收到其他插件的线路 ID。

正确:args.serverId = "source-backup",resolverServers = { "fixture.resolver": "resolver-backup" },各阶段只收到自己的原始 ID。

直播与图文的调用约定

适用范围

对应 05 §9、06 §8/9、08 首页与详情界面、09 媒体归属。首页按具体方法显示来源的直播/图文入口,停用来源不显示。

调用签名

参数与资源契约

直播只向 live.resolve 传原始 channelId、播放参数,不构造 mediaId/episodeId。节目单使用 Unix 毫秒窗口,用户打开后才请求未来 24 小时。 图文播放重新请求 articles.getDetail({id:articleId}),仅取其中 streams 进入共享媒体链;正文/作者等字段不进入播放配置。 图文评论与互动分别使用 {kind:"article",articleId},不复用 media 目标。两类播放沿用来源/解析器隔离、取消、修订和字幕归属,无新增环境变量。

校验与失败处理

条件 结果
source 只有完整 live 正常启用,不要求空壳 media
live/articles 命名空间缺少必需方法 初始化失败,保留可查看的安装记录
频道非法 ID/number,节目时间缺失或倒置 丢弃独立坏条目,记录路径
图文正文不是文本 结果校验失败
正文超过 512 KiB 按 UTF-8 字符边界截断并警告
streams 为 [] 正文正常显示,不显示视频入口
节目单失败 不影响频道播放,提供独立重试
播放致命错误 尝试一次 reason:refresh,失败后允许手动切换/重试

正常与异常用例

必须验证的行为

tests/extra_content.rs 覆盖纯直播初始化、缺失方法、非法独立条目、正文预算、空 streams、媒体会话与公开选择描述。 scripts/test-plugin-extra-content.mjs 覆盖生产 App 的首页入口、频道/文章分页、HLS/普通视频、节目单按需请求、原始参数归属、正文清洗及外链命令。 列表封面集中登记图片批次,避免每行占用一个媒体会话;超出 200 个图片的集合分批登记并在页面销毁时释放全部会话。

实现示例对照

错误:直播插件伪造 VOD episodeId;文章 HTML 原样放入界面并保留上游 img src。

正确:频道仅传 channelId;文章使用惰性模板解析,再从允许的节点/属性构建 React 元素,图片统一替换为 assets.prepare 返回的本地地址。

跨来源确认与片库关联

对应 06 §17.2、08 §6/7,不改变插件 search/media 的公开签名。用户打开换源面板后按当前标题并行查询其他已启用、可见且实现 search/media 的来源;年份与类型用于宿主排序,不增加插件未约定的搜索参数。

关联命令

确认后播放

候选详情所有已返回播放列表中,明确的 season + episodeNumber 仅匹配一集且无未加载分页时可预选;双方都缺少 season 时允许按明确 episodeNumber 预选。index 相同、身份缺失、多个匹配或分集尚未全部返回时不自动选集。

用户可手动选新来源分集、加载其后续页,或只打开新来源详情。锁定分集不可选。 跨页选择只保存真实 playlistId/episodeId;新页重新取详情并沿原始游标查找该分集,重复游标终止,分集变化时停止并要求重选。

目标分集已有本地进度时优先使用目标记录。没有时,“沿用原来源播放位置”默认不勾选;用户勾选才复制确认时有限、非负的秒数,否则从 0 开始。 确认前先保存原来源当前进度,再建立关联并替换当前详情路由;取消、搜索、查看候选均不建立关联,也不触发新来源解析。

校验与失败边界

情况 行为
关联任一条目标识或 MediaItem 非法 INVALID_ARGUMENT,两侧记录与磁盘均不变
关联到自身 INVALID_ARGUMENT
一个来源搜索失败 本来源独立错误与重试,其他候选可继续选择
候选详情或分集页失败 留在当前来源,显示重试,不解析候选媒体
新来源进度读取失败 停止确认播放,避免在未知优先记录时覆盖进度
关联保存失败 保留原详情与播放,显示失败
新来源分集已移除、锁定或重复游标后仍找不到 不自动播放,提示用户重新选集
插件移除 关联与旧片库保留,界面标记来源不可用

正常案例:A 的 S2E1 → B 的明确 S2E1(即使 index 为 41),用户确认后以 B 的真实 ID 播放。基线案例:不勾选沿用位置且 B 无进度,则从 0 播放。异常案例:B 中有两条 S2E1,要求手动选集;同 index 不视为匹配。

Rust content_library 检查关联组对称/合并/幂等、重启恢复、非法写入原子性,以及各来源收藏/追更/进度独立。scripts/test-plugin-source-switch.mjs 使用真实 QuickJS 与生产界面检查排序、失败隔离、取消、季集匹配、进度优先级、显式沿用秒数、跨页选集及“我的”关联入口。

错误示例:根据旧 index:1 把新来源第一个条目视为同集,并直接覆盖旧记录。 正确示例:只用明确季集身份提出候选,由用户确认新来源及分集,各来源继续按自己的真实 ID 记录。

信息流的调用约定

对应 05 §3、06 §5、08 §7 与 09 媒体会话约定,不新增插件能力或字段。

首页编排与滚动分页

对应 08 §3–5 与 03 §3,不新增插件方法。

按播放站点记住解析器

对应 06 §3、08 §7/8,不更改插件的 PlaybackParams 或解析器参数。

宿主命令与公开选择描述

优先级与持久化

  1. 流声明的 resolverId 优先,指定插件不可用时明确失败,不自动改用其他解析器。
  2. 本次对当前主机的手动选择优先于记忆。
  3. 未手动指定时,从可用且 matches 命中的候选里取该精确主机的记忆;停用、移除或不匹配的记忆忽略。唯一匹配直接使用,多个匹配且无可用记忆则要求选择。
  4. 没有匹配时列出已安装可用解析器供手动选择;不将匹配其他主机的历史偏好自动套用。
  5. “记住这个站点的选择”默认关闭,仅本次解析成功、实际提供者与所选项一致且主机未变化时保存;失败或取消不覆盖旧记忆。插件指定解析器时不提供覆盖记忆入口。

点播详情与直播/图文/信息流共用该选择流程;设置提供逐项忘记与全部清空。 tests/resolver_preferences.rs 验证精确主机隔离、跨宿主重启、指定优先、失效偏好和落盘失败; scripts/test-plugin-resolver-preferences.mjs 验证实际点播/直播、默认不记住、跨站点切集、失败不覆盖、页面重载与设置清理。

验证与复现

在仓库根目录执行:

cargo test --manifest-path src-tauri/crates/hpp/Cargo.toml
cargo check --manifest-path src-tauri/Cargo.toml
cargo build --manifest-path src-tauri/Cargo.toml
npm.cmd run build
npm.cmd run test:plugins

依赖已缓存时可以为 Cargo 添加 --offline。浏览器测试默认使用 Windows Edge; 其他安装位置可设置 BROWSER_EXECUTABLE。测试数据目录临时创建,不读取正式插件、登录态或上游。 exFAT 工作目录不支持硬链接;如 MSVC 报增量缓存调试信息损坏,可设置 $env:CARGO_INCREMENTAL = '0' 后重新构建。资源紧张时为 Cargo 添加 -j 2。

test:plugins 运行真实 Rust/QuickJS 宿主和生产 App 与 React 页面,只将 Tauri IPC 换成 stdio 传输。 浏览器本地录制合成 H.264 画面,再经实际媒体代理验证普通视频拖动、HLS 初始化段/分片播放及刷新。 新增回归覆盖新导航、首页、分类筛选/排序、多来源搜索合并与单源失败隔离、本地收藏/追更、实际分集续播。 同时使用 src-tauri/crates/hpp/tests/fixtures/forms.js 验证账号/操作表单、多步登录、登录态恢复与失效、 退出接口失败后的本地会话清理,以及离开面板取消提交。注册入口只验证通用外链命令,不访问外部站点。 src-tauri/crates/hpp/tests/fixtures/social.js 与 scripts/test-plugin-social.mjs 验证评论/回复、云端互动、目录与关注分页、取消评论,以及实际播放事件回报;心跳使用可注入的单调时钟验证 30 秒边界。 直播与图文回归使用 src-tauri/crates/hpp/tests/fixtures/extra-content.js;纯直播源不声明点播 media,以防测试空壳掩盖初始化约束。 本地私有开发基线独立验证,不纳入客户端、公共测试或文档站产物。 产物位于忽略提交的 test-results/plugins/<时间>/,包含测试报告、截图和本地请求记录。 这项测试不等同于实际 Tauri WebView2 窗口/打包验证。

历史验证(2026-11-13,早期界面)

2026-11-15 审计与经典界面回归

当前桌面首页与详情入口共用经典沉浸播放器,选集、评论、详情通过抽屉打开;手机沿用文字底栏与播放器手势。信息流 entryEpisodeId 直接解析,打开详情/选集抽屉时才请求详情,不人为添加 playlistId。错误提示不遮挡收藏和选集。

本轮 Rust 全套(含 runtime_contract 与 public_example)通过;经典 UI 的社交、直播/图文、信息流、换源和解析器专项回归通过。Android content URI 文件导入修复后已实测;最终 APK 粘贴/文件导入及单部真实 MP4(1920×804)播放通过。完整前端 25 组、Rust 52 项通过。剩余缺口见本文限制说明,不把历史界面测试当作当前验收。

引擎补丁

rquickjs-sys 0.12.2 的普通 Promise reaction/await 未提供完整 Before/After 钩子。 本地 vendor 补丁为每条 continuation 捕获注册上下文,避免并发调用或共享 Promise 时串用 requestId。 详见 src-tauri/vendor/rquickjs-sys/HGHUB-PATCH.md;升级引擎时必须保留相应回归测试。