Vincent's Essays

博客园 首页 新随笔 联系 订阅 管理

Memoria 开发记录 14:让故事可以重现——从临时缓存到可复现的数字资产

前言

Memoria 不只是浏览照片,还会把照片、音乐、节拍和渲染参数组合成故事视频。早期实现中,导出结果和音乐主要依赖文件路径与缓存目录。这样的方案在一次运行中可用,但应用清理缓存、路径变化或设备重启后,故事就可能无法再次播放。

这暴露出一个本质问题:

一个已经由用户保存的故事,究竟是一组临时文件引用,还是一个可以长期恢复和重新生成的数字资产?

围绕这个问题,项目完成了 ObjectBox 数据迁移、视频参数固化、音乐二进制保存、SHA256 去重和压缩。目标是让故事不依赖偶然存在的缓存环境。

数据库迁移:从替换存储引擎到重建事实来源

提交 40c9c4d 是一次大规模 ObjectBox 迁移,涉及实体模型、媒体资产同步、向量索引、照片服务、事件、故事和多个页面。单次改动新增约 2700 行、删除约 1200 行。

数据库迁移最危险的地方,是为了兼容旧接口而长期保留一层“假装还是旧数据库”的适配层。提交中曾加入 isar_compat.dart,随后 d8f671b 在修复 APK 构建时将其删除。

这说明迁移最终必须明确新的事实来源。兼容层适合帮助过渡,但如果长期存在,会让开发者无法判断某个查询究竟遵守哪套事务、索引和实体语义。

ObjectBox 在这里不仅保存普通业务字段,还承载:

  • 照片和媒体资产身份;
  • 图片与人脸嵌入向量;
  • AI 分析状态;
  • 故事、视频和音乐的固化信息;
  • 可恢复任务状态。

它逐渐成为端侧数据资产的中心,而不只是一个更快的键值存储。

为什么缓存路径不能代表故事

一个视频缓存路径可能因为以下原因失效:

  • 系统清理应用缓存;
  • 用户手动执行缓存清理;
  • 文件名或缓存键算法调整;
  • 导出目录在不同平台上变化;
  • 故事参数更新后旧缓存与新内容不一致。

如果 StoryEntity 只记录路径,数据库中保存的其实只是一个易失指针。真正能够描述故事的,是生成它所需的输入和参数。

提交 9ea5132 将以下信息固化到故事实体:

  • 已缓存视频路径与缓存键;
  • 自定义音乐路径;
  • 动态节拍数据;
  • 视频渲染参数。

播放和分享时优先复用已生成视频;如果视频不存在,仍可以依据参数重新生成。这使缓存从“唯一结果”降级为“可复用派生物”。

音乐为什么要保存二进制

用户选择的自定义音乐可能来自临时选择器路径、下载目录或其他应用提供的 URI。只记录路径,并不能保证未来仍有权限读取。

提交 b908ae2 将音乐文件二进制保存进 ObjectBox,并通过 SHA256 建立去重缓存:

选择音乐
  ↓
读取二进制并计算 SHA256
  ↓
保存到故事实体
  ↓
需要播放或导出时,按哈希恢复到 MusicCache
  ↓
同一内容不重复写入

SHA256 在这里不是为了密码学安全,而是作为内容寻址键。文件名可以变化,路径可以变化,只要内容相同,哈希就相同。这样多个故事引用同一首音乐时,无需反复恢复副本。

压缩:持久化能力也要控制成本

直接把音乐二进制放入数据库会增加体积。提交 d18b269 引入 Zstandard 压缩并优化音乐存储与去重逻辑。

Zstandard 的优势是压缩率和解压速度平衡较好。不过,音频文件本身往往已经压缩,因此实际收益取决于格式。工程上不能假设“加了压缩就一定更小”,而应记录压缩前后大小,并在收益不足时保留原数据。

更重要的是,压缩逻辑必须对业务透明:

  • 保存时决定是否压缩;
  • 实体记录编码方式;
  • 读取时自动还原;
  • 上层渲染和播放只接收正常文件或字节。

缓存键决定结果是否可复用

提交 013d389 重构视频缓存管理、缓存键生成和路径获取。一个正确的缓存键应该覆盖所有会影响输出的输入,例如:

照片顺序 + 转场参数 + 字幕 + 音乐内容 + 节拍数据 + 分辨率 + 帧率

如果缓存键漏掉音乐或渲染参数,用户修改设置后仍可能拿到旧视频;如果缓存键包含不稳定路径,相同内容又无法命中缓存。

因此,缓存键应尽量基于稳定内容身份和规范化参数,而不是临时路径或对象地址。

保存、导出、分享是一条完整链路

提交 bcaadd9 重构视频分享和文件管理。故事固化的最终价值,需要在用户操作中体现:

  • 已有缓存视频时直接播放;
  • 分享时优先复用已导出的文件;
  • 缓存失效时能够重新生成;
  • 用户手动保存的故事才进入长期故事列表;
  • 导出完成后,故事实体同步记录最新结果。

这条链路将“生成一次视频”提升为“管理一项可长期使用的作品”。

总结

让故事可复现,本质上是一次数据所有权重构:数据库保存生成事实,缓存只负责加速,文件路径不再承担长期身份。

最终形成的原则是:

  • 新数据库应成为明确事实来源,兼容层只用于短期迁移;
  • 故事要保存可重建输出的参数,而不只是缓存路径;
  • 外部音乐需要固化,避免未来权限和路径失效;
  • 使用内容哈希去重并建立稳定缓存键;
  • 压缩策略必须可识别、可还原并验证实际收益;
  • 播放、导出和分享都围绕同一故事实体工作。

对应提交:40c9c4dd8f671b013d3899ea5132b908ae2d18b269bcaadd9

posted on 2026-06-14 23:20  Vincentson  阅读(0)  评论(0)    收藏  举报