OpenScreen 数据模型深度剖析:AxcutDocument 单一数据源与版本迁移机制全解
【免费下载链接】openscreenRecord your screen, ship a demo. Free and open-source, GPU-accelerated, no watermarks, no subscriptions. Windows, macOS, Linux. Actively maintained.项目地址: https://gitcode.com/gh_mirrors/opens/openscreen
OpenScreen 是一款免费开源、GPU 加速、无水印的屏幕录制与视频演示工具(Windows / macOS / Linux)。它的编辑能力建立在一个核心设计之上:AxcutDocument单一数据源。本文用通俗语言拆解这套数据模型的字段构成、"只进不退"的版本迁移机制和双层持久化架构,帮你在不读大量源码的情况下,理解 OpenScreen 如何保证每一个项目文件都完整、可升级、可撤销。
什么是 AxcutDocument?项目的"唯一真相"
在 OpenScreen 里,一个项目 = 一个文档对象 = 磁盘上的一个 JSON 文件。这个文档类型就是AxcutDocument,它用 Zod 类型校验定义,是整个编辑器所有界面(时间轴、预览、字幕、AI 智能体)共同读写的单一数据源(Single Source of Truth)。
它的当前版本号schemaVersion是8,定义在 src/lib/ai-edition/schema/index.ts 顶部:
export const axcutSchemaVersion = 8;顶层字段一览
文档的完整结构声明在 schema/index.ts 的documentSchemaShape中,核心字段如下:
| 字段 | 承载内容 | 小白解读 |
|---|---|---|
schemaVersion | 固定为8 | 版本"身份证",迁移的起点 |
project | id、标题、创建/更新时间 | 一个文件对应一个项目 |
assets[] | 每条录制/导入的媒体素材 | 含原始路径、摄像头轨道、时长 |
timeline | 剪辑 clips、空隙、剪切、静音、变速、字幕区间 | 时间轴的骨架 |
annotations[] | 文本、模糊等标注覆盖层 | 锚定在某个 clip 上 |
zoomRanges[] | 放大特效(深度 1–6) | 同样锚定 clip |
audioTracks[] | 导入的旁白/背景音乐/音效 | 按时间轴秒数定位 |
transcripts[] | 每条素材的语音转写文本 | 字幕层真正读取的数据 |
legacyEditor | 旧版编辑器的外观/光标设置 | 无损兼容老项目 |
一句话:你在界面上的每一次操作,最终都只是对这个 JSON 的一次修改。
版本迁移机制:为什么老项目永远不会打不开?
这是 OpenScreen 数据模型最精彩的部分。官方文档在 technical-documentation/architecture/document-model.md 中明确了两条铁律:
- 单向、只前进:迁移只有"升版"路径,没有降级。新版文档落到旧版程序上会被直接拒绝,而不是被悄悄截断——宁可报错,不丢数据。
- 加载时执行:所有升级链在读取文件时一次性跑完(
migrateRawDocumentToCurrent),之后的内存解析只是一次轻量的字面量校验,避免每次保存都重复跑整条迁移链的开销。
v3 → v8 的演进故事
每一次版本号提升,背后都解决一个真实问题:
| 版本 | 升级做了什么 | 为什么 |
|---|---|---|
| v3 → v4 | 摄像头轨道cameraTrack从文档根移到所属素材上 | 多剪辑项目里每条素材可以有自己的摄像头 |
| v4 → v5 | 放大/标注/变速等区域变成"剪辑锚定"片段 | 同一素材出现两次时,特效不再张冠李戴 |
| v5 → v6 | 废弃"native"宽高比,烘焙成具体"W:H" | 让解析结果永久确定(尺寸未知时留待下次加载再转,绝不乱猜) |
| v6 → v7 | 剪切(trim)也加入clipId锚定 | 复制的 clip 上的剪切互不串扰 |
| v7 → v8 | "Auto"成为默认宽高比,旧文档写入"16:9" | 保证老项目画面在升级后一帧都不变 |
这些升级器以函数嵌套的方式串成一条链,集中在 schema/index.ts 的migrateRawDocumentToCurrent:
export function migrateRawDocumentToCurrent(raw: unknown): unknown { return readFollowCursorAsAutoOrbit( raiseInvertedTranscriptEnds( dropAudioAnchoredTrims( liftZoomClickImpact( upgradeV7DocumentToV8( upgradeV6DocumentToV7( upgradeV5DocumentToV6(upgradeV4DocumentToV5(upgradeV3DocumentToV4(raw))) ) ), ), ), ), ); }链外还有一道兜底闸门:documentSchema中的z.literal(axcutSchemaVersion)检查会拒绝任何未知版本——不认识的文件不会被半吊子地读进来。
更老的 v2 项目怎么办?
OpenScreen 合并前的旧编辑器保存的是另一种格式(EditorProjectData)。首次在新编辑器中打开时,纯函数migrateProjectDataToAxcutDocument(src/lib/ai-edition/document/migrate.ts)会把它翻译成当前结构:单条录屏变成"一条素材 + 一个剪辑",旧剪切区间按原语义平移,未纳入新模型的约 20 个外观设置原样塞进legacyEditor包裹层——关闭再打开 AI 编辑模式也不会丢任何设置。这个小技巧很妙:函数故意输出一篇"v4 草稿"再走完整升级链,确保 v4→v5 的锚定逻辑一定被执行到。
双层持久化:谁来读、谁来写?
OpenScreen 的文档字节恰好只有两个"主人",职责严格分离:
| 渲染进程(界面层) | 主进程(落盘层) | |
|---|---|---|
| 代码 | src/lib/ai-edition/store/projectStore.ts | electron/ai-edition/document-service.ts |
| 职责 | 持有活动文档、播放状态、dirty脏标记 | 读、写、列出、增删素材 |
| 磁盘位置 | — | userData/projects/<id>.openscreen,一个项目一个 JSON |
两个工程细节值得称道:
- 原子写入:每次保存走"临时文件 + 重命名",并为每个项目维护写入队列串行化,程序崩溃也绝不会出现写了一半的文件(document-service.ts 的
saveProject)。 - 扩展名自动迁移:更早的版本把这些文档存成
.axcut,现在的服务在首次访问时会静默改名为.openscreen——依据的是 JSON 里的版本号而非文件名,文件内容一个字节都不动。
两层读取路径都必须先过migrateRawDocumentToCurrent再documentSchema.parse,所以无论磁盘上是 v3 还是 v8 的老文档,进入内存时都已经是最新的AxcutDocument形态。
撤销/重做:50 步快照栈
你可能会问:那"撤销"是怎么和这份单一数据源配合的?
OpenScreen 没有把撤销历史写进文件,而是在渲染进程维护了一个有界快照栈(src/lib/ai-edition/store/undo.ts):
past[]/future[]最多各保留50份文档快照(MAX_HISTORY = 50);- 每次
setDocument把"即将被替换的文档"压入past,Ctrl+Z / Cmd+Shift+Z / Ctrl+Y 沿栈游走; - 因为 AI 智能体的每次工具调用也走
setDocument,所以"撤销一整轮 AI 修改"天然按调用顺序逐一回退。
好处很直接:撤销历史不会让项目文件无限膨胀,文件里只保留当前文档本身。
小结:这套设计给你带来了什么
- 放心升级:版本迁移单向链 + 未知版本拒绝,老项目打开即自动升版,画面一帧不变;
- 不怕损坏:原子写入 + 写队列,中途断电也不留半截文件;
- 操作可逆:50 步内存快照栈,人肉修改和 AI 修改都能撤销;
- 单一数据源:界面、预览、字幕、AI 智能体共享同一份文档,不会出现"时间轴和预览打架"。
想继续深挖,推荐阅读官方架构文档 technical-documentation/architecture/document-model.md 与 technical-documentation/architecture/timeline-model.md,以及迁移逻辑的单元测试 src/lib/ai-edition/document/migrate.test.ts——那里面有每个版本迁移的边界用例。
【免费下载链接】openscreenRecord your screen, ship a demo. Free and open-source, GPU-accelerated, no watermarks, no subscriptions. Windows, macOS, Linux. Actively maintained.项目地址: https://gitcode.com/gh_mirrors/opens/openscreen
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考