@speclip/pi-subtitles
Agent-reviewed subtitle timing, layout, and standard track export for Pi
Package details
Install @speclip/pi-subtitles from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@speclip/pi-subtitles- Package
@speclip/pi-subtitles- Version
0.3.1- Published
- Sep 10, 2026
- Downloads
- 271/mo · 200/wk
- Author
- tapcli
- License
- MIT
- Types
- extension, skill, prompt
- Size
- 95.2 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions/subtitles/index.ts"
],
"skills": [
"./skills"
],
"prompts": [
"./prompts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@speclip/pi-subtitles
给 Pi 使用的字幕决策插件包。它把 ASR 词级时间戳映射到完成 A-roll 剪辑后的成片时间轴,由 Agent 审阅断句、标点和换行,再导出可追溯的 UTF-8 SRT / ASS 字幕轨道。
它不直接烧录视频,也不把任意 FFmpeg 参数塞进 Agent 工具。字幕判断与视频渲染保持分离:本包负责“字幕写什么、何时出现、如何分行”,支持字幕轨道的渲染器负责最终画面合成与视觉验收。
能力边界
- 输入:
pi-speech兼容的词级 JSON 转录,也兼容常见{ word, start, end }秒级结构。 - A-roll 映射:直接读取渲染回执,支持多源、回接、重复片段和实际帧/采样边界;生成、修订及导出均检查剪辑点。
- 自动分段:结合标点、停顿、字数、时长和阅读速度生成初稿。
- Agent 审阅:通过不可变 revision 修正 ASR 文本、标点和断句;横屏项目强制单行,审阅阶段也不能绕过。
- 导出:标准 SRT;可选带横屏/竖屏安全区预设的 ASS;两者都不覆盖已有文件。
- 溯源:绑定原转录哈希、精确 revision、输出哈希和持久化导出回执。
B-roll 只覆盖画面而保留 A-roll 主音频时,不会改变字幕时间。若后续改变了语速、主音频或 A-roll 片段顺序,应新建字幕项目。
环境要求
- Node.js 22.19+
- Pi 0.84.1–0.85.x
- 一份含词级时间戳的 JSON 转录
安装与验证
npm install
npm run check
pi install ./
也可以安装发布包:
pi install npm:@speclip/pi-subtitles
工具工作流
1. 创建字幕项目
A-roll 已渲染时,优先直接传官方回执和原始转录,不需要自行生成派生转录:
subtitles_create {
projectId: "launch-captions",
renderReceiptPath: "analysis/render_receipt.json",
transcriptPath: "transcripts/launch.json",
layoutPreset: "landscape",
protectedPhrases: ["跷跷板"]
}
回执必须含 pi-media 的 timelineTiming,工具核对成片和源文件哈希,按实际输出边界映射。回接和重复片段分别保留独立字词 ID;帧取整造成的字尾截短返回 render-tail-clipped 听校提示,真正跨原片剪点的词仍会报错。
多个主素材用 sourceTranscripts: [{ sourcePath, transcriptPath }] 替代单个 transcriptPath,每个主素材恰好绑定一份转录。回执模式不能同时传 sourceDurationMs 或 timelineSegments。
以下是兼容保留的旧输入方式;它没有经过渲染校验的跨插件时间轴绑定。
无 A-roll 剪辑时,只传源时长:
subtitles_create {
projectId: "launch-captions",
transcriptPath: "transcripts/launch.json",
sourceDurationMs: 93224,
layoutPreset: "landscape"
}
旧模式有剪辑时,传按播放顺序排列的保留源区间,可回接、可重复:
subtitles_create {
projectId: "launch-captions",
transcriptPath: "transcripts/launch.json",
sourceDurationMs: 93224,
timelineSegments: [
{ id: "a-001", sourceStartMs: 0, sourceEndMs: 12400 },
{ id: "a-002", sourceStartMs: 13100, sourceEndMs: 28700 },
{ id: "a-003", sourceStartMs: 29400, sourceEndMs: 93224 }
]
}
计算关系为:
成片时间 = 前面所有保留片段的累计时长 + 词源时间 - 当前片段源起点
若一个词跨过剪辑边界,工具会拒绝创建,要求回到 A-roll 调整到词间安全边界。
2. 分页审阅全部字幕
subtitles_get {
projectId: "launch-captions",
offset: 0,
limit: 100
}
自动结果只是初稿。Agent 应结合实际音频检查文本、术语、标点、语义分组、阅读速度和画幅布局,并继续翻页直到 hasMore: false。
layoutPreset 默认为 landscape:横屏字幕严格单行,每条最多 16 个可见字符,目标 8–16 字。portrait 允许最多两行。两种画幅都以 1.5–4.5 秒、约 9 字/秒为节奏参考;这些节奏值用于找自然断点,不要求每条机械一致。
3. 写入 Agent 审阅版本
少量改字、拆句或合句,优先使用局部 patches,不必重传全部字幕:
subtitles_apply {
projectId: "launch-captions",
expectedRevision: 1,
changeReason: "听校后修正术语",
compact: true,
patches: [
{ fromCueId: "cue-021", text: "如果你买256GB的", correctionReason: "对照该句原声确认容量" }
]
}
文字补丁保留原时间,仅作用于一条字幕。局部拆分/合并时,先用 subtitles_get { projectId, view: "words", cueId: "cue-021" } 获取该条的字词;以 patches: [{ fromCueId, toCueId, groups: [...] }] 提交目标范围的完整词分组。工具自动计算时间,保留其他字幕;相交补丁、漏词、跨剪点和未知 ID 会整体拒绝。所有补丁 ID 均针对 expectedRevision,成功后重新读取新 ID。组内默认文字来自原 ASR,已有听校修正需通过 text 和 correctionReason 保留。详见局部审阅接口。
需要全片重新分组时,读取 subtitles_get { projectId, view: "words", offset: 0, limit: 200 } 的所有字词页,按稳定 ID 提交完整分组,让工具计算时间:
subtitles_apply {
projectId: "launch-captions",
expectedRevision: 1,
changeReason: "按语义重新断句,保持全部字词",
groups: [
{ startWordId: "a-001:word-0", endWordId: "a-001:word-8" }
// 继续列出全部保留字词,不能只提交一页
]
}
分组不得遗漏、重复、乱序或跨剪辑片段。可选 text 用于标点、空格或听校后的纠错;实质文字变化必须说明 correctionReason。工具按组内真实字词时间生成字幕,短字幕仅借用同片段内的静音。protectedPhrases 用于初稿的短语保护,原 ASR 多字词也优先保持整体。
顶层 patches、groups 与下方兼容的完整 cues 三选一。直接提交 cues 可精确改显示时间,但文字覆盖变化会产生 text-changed 提示。所有修订均校验非重叠、片段边界、视频末端和行长。
subtitles_apply {
projectId: "launch-captions",
expectedRevision: 1,
changeReason: "核对音频后修正产品名和语义标点",
cues: [
{ id: "cue-001", beginMs: 320, endMs: 2840, text: "这是修正后的第一条字幕。" }
]
}
cues 必须是完整列表,不是某一页。时间必须递增、不重叠,且不能超过成片时长。expectedRevision 防止并发审阅互相覆盖;每次成功都会产生新的不可变快照。
subtitles_get 同时返回 findings 和 review。阅读速度、短时显示等是审阅提示;不能用它们掩盖结构错误。review.text、review.sync、review.visual 分别记录听校、同步和视觉结果,可在修订时传 { status: "passed", evidence: "实际核对的音频/视频及范围" };未提供的项目会重置为 pending。结构通过不代表听校通过。
四个工具均可显式传 compact: true 减少重复输出;默认保留原有完整响应。create/apply 返回版本、哈希、字幕总数和诊断计数;export 保留完整导出回执与 subtitleTrack;get 省略完整项目并只附当前字幕页相关诊断。使用 subtitles_get { view: "findings", offset, limit } 阅读全部诊断,包含全局字尾截短等提示。view: "words" 正确返回 words,其 total、hasMore 均以词数计算;指定 cueId 后以筛选结果计算分页。
4. 导出标准轨道
subtitles_export {
projectId: "launch-captions",
revision: 2,
srtPath: "deliverables/launch.zh-CN.srt",
assPath: "deliverables/launch.zh-CN.ass"
}
工具会按项目画幅自动使用安全区样式,并返回 SRT / ASS 文件引用、SHA-256 和持久化导出回执。横屏使用固定 1920×1080 ASS 设计坐标,竖屏使用 1080×1920;这与源视频是 1080p 还是 4K 无关,libass 会等比缩放。不要把 4K 源分辨率直接写成 ASS 的 PlayRes。
省略 assStyle 即使用默认样式。需要微调前,调用 subtitles_get { projectId, view: "style" } 获取当前画幅的默认值、固定字段和真实数值范围,无需查源码或试错。
横屏和竖屏统一使用 Source Han Sans SC Heavy。白色字形不再附着黑色描边,而是使用向右下偏移的半透明黑色柔化阴影;横屏保持下方安全区,竖屏仍上移并扩大右边距,避开常见交互区。字体、阴影和几何参数只能在安全范围内微调。渲染环境必须真实安装该字体,不能依赖静默字体回退。
本包的完成状态是“字幕轨道已生成并校验”,不是“字幕已经烧进视频”。最后应把轨道交给支持字幕的渲染器,并检查实际成片中的同步、遮挡、安全区、字体回退和漏字。
5. 预览与烧录交接
导出返回 subtitleTrack: { sourcePath, format, mode: "burn-in", exportReceiptPath }(回执模式才有 exportReceiptPath)。直接放入 pi-media 的 timeline edit_apply,保留其他编辑字段。渲染器核对轨道哈希和主音频时间轴指纹;B-roll/BGM 调整不改变该指纹,主素材、源区间、顺序或输出帧率改变则拒绝旧字幕。
先用支持新接口的 pi-media 生成抽样证据:
media_subtitle_preview {
videoPath: "deliverables/clean.mp4",
exportReceiptPath: ".subtitles/projects/launch-captions/exports/<exportId>.json",
sampleTimesSeconds: [5, 55, 77],
clip: { startSeconds: 54, durationSeconds: 4 }
}
选择视频内 1–6 个时间点,短视频最长 15 秒。返回 PNG、可听短视频、字体选择日志和报告路径;视觉与同步状态保持 pending,由实际查看/听校后更新。最终视频仍需 render + review。字幕渲染环境需有 libass;可用 PI_MEDIA_SUBTITLE_FFMPEG_BINARY 指定二进制。
内部保留精确毫秒,导出时 SRT 量化为毫秒、ASS 为厘秒,且时间不能越过镜头边界。格式精度导致零时长时明确失败,不静默输出坏字幕。
工作区数据
项目状态保存在:
.subtitles/projects/<projectId>/
├── project.json
├── words.json # 不可变字词映射、源字词位置、片段边界与诊断
├── snapshots/<revision>.json
└── exports/<exportId>.json
新字段均为可选字段,旧项目与原有四个工具仍兼容;升级不会原地改写旧项目。源转录与导出文件都不会被覆盖。源转录字节变化后,旧项目会拒绝继续导出,避免把新内容误配到旧时间轴。
开发
npm ci
npm run check
npm pack --dry-run
GitHub Release 标签必须与 package.json 完全一致,例如 v0.2.1。发布工作流会在标签快照上重新执行检查并通过 npm OIDC 发布。
License
MIT