跳转到正文
BiliBili ShadowReplay
开发文档后端开发

存储自带的 JSON 录播目录

每个存储保存自己的当前录播元数据,主 SQLite 只维护可重建的聚合索引。 本地、S3 与 WebDAV 使用同一种 JSON 格式,不在对象存储中运行数据库。

文件布局

.bsr/storage.json                         存储 UUID、格式版本 2
.bsr/records/<录播 UUID>/recording.json    每条录播的一份当前元数据
.bsr/import.json                          旧 SQLite 记录的导入进度(如有)
.bsr/writer.lock                          本地文件锁(仅本地存储)

媒体路径不变。元数据集中在 .bsr/records/,避免转移媒体时将未发布的元数据 一并复制到目标。JSON 保存标题、时间、时长、大小、封面引用、已完成摘要等, 以及当前状态和导入/转移重试凭据。修改覆盖当前文件,不生成版本链。

单实例写入

同一远端存储只能由一个 BSR 实例写入。 不做远端锁、心跳、租约过期或自动 接管,也不保证多台机器同时录制、编辑、删除或转移时的数据安全。 设置中会提示该限制。请停止原实例后再切换写入机器。

进程内按存储 UUID 防止重复打开 writer,本地存储另有操作系统文件锁。 只读实例可展示和播放,不负责初始化或清理。凭证仍只从应用设置读取。

挂载与查询

  • 添加存储时扫描当前 JSON,完整校验后重建 SQLite 索引。
  • 移除是卸载:隐藏列表和统计,移除本机配置,不删除存储内容。
  • 重挂依据存储 UUID 恢复,不需要永久保留已移除的配置名称。
  • 日常列表、筛选和统计查询 SQLite,不反复扫描 S3。
  • 读取、解析或列举失败保留上一次完整缓存,不能将故障当作空目录。
  • 刷新仍存在的录播时,保留本机 Summary 任务的运行/失败状态、转写缓存和任务关联; 只对已完成结果按存储目录重新同步。录播实际移除时才删除其操作状态。
  • 首次加载与录播数量成正比,目前先构建内存视图,再分批发布 SQLite 索引。 大型目录的索引快照加速是后续优化,不是当前远端数据库。
  • 当前删除标记也保存递增版本,防止旧扫描结果覆盖较新的删除。

发布、删除与转移

本地 JSON 通过临时文件、fsync 和原子替换发布;远端使用普通 PUT,随后读回 核对内容。写入超时但读回匹配时可以确认成功;无法确认时停止该 writer, 关闭重开后再恢复,不盲目重复累计更新。

删除顺序:

  1. 写入 JSON 删除标记。
  2. 清理媒体文件。
  3. 确认清理完成,更新本机索引。

崩溃或文件清理失败后,删除标记作为持久化清理队列,在可写重挂时继续处理。 完成后保留小型当前标记,不保留编辑历史,也不自动执行元数据 GC。

转移沿用本地任务日志:

  1. 检查两端可写、目标无冲突。
  2. 复制或移动媒体,校验目标文件。
  3. 保存目录转移意图,在目标发布 JSON 和接收凭据。
  4. 在来源保存转移状态和退役凭据。
  5. 提交本机绑定,再清理源文件。

允许中断后暂存两份,不承诺跨存储原子事务。S3 文件发布改用存在性检查加普通 流式 PUT,在应用单写者和转移操作锁范围内拒绝已存在目标;它不是跨机器原子 不覆盖。每个流式上传对象仍限制 5 GiB。转移中不要通过其他工具改动对象。

格式与已有 SQLite 记录导入

主 SQLite 的旧记录导入继续使用冻结快照、固定 UUID 和内容校验。 录播 JSON 与逐条接收凭据一起发布,再推进导入进度,失败可续传; 未完成导入不能发布聚合索引。

当前仅支持 JSON 格式 2。读取、写入或显式初始化遇到其他格式时均拒绝操作, 不会覆盖原内容或创建空目录代替。修订号直接取当前录播 JSON 的最大值, 包括删除和转移标记,不再依赖额外的修订号下限或数据库备份。

检查 S3 目录

在配置的对象前缀下面查看 .bsr/storage.json:

  • format_version: 2:当前支持的格式。非空目录的当前记录在 .bsr/records/<UUID>/recording.json;空目录可能没有 .bsr/records/ 对象。
  • 其他版本:不支持该格式。查看设置页错误或日志中的 Storage catalog <名称> unavailable,不要手动修改版本号。
  • 没有 storage.json:先确认 bucket 和对象前缀;不要手动补写格式标记, 也不要删除目录来强制初始化。

验证

cargo test --manifest-path src-tauri/Cargo.toml -p recording_catalog --locked
cargo test --manifest-path src-tauri/Cargo.toml -p recording_storage --locked
cargo test --manifest-path src-tauri/Cargo.toml -p bili-shadowreplay \
  --no-default-features --features headless database::catalog --locked
python3 scripts/test-json-catalog-rustfs.py

RustFS 脚本使用本机已有镜像,创建只绑定 loopback 的临时容器和随机测试凭证, 运行完自动移除容器,不访问现有 RustFS 或应用存储。它覆盖实际 S3 文件读写、 local ↔ S3、S3 ↔ S3、卸载重挂与转移恢复, 不等于真实 OSS/COS 验收。

本页内容