播客动态图形处理
Motion 5.9 没有 AppleScript 字典,也没有命令行渲染器。这里的所有操作都通过将 Motion 文件视为 XML (ozml),以及生成引用已安装标题模板的 Final Cut Pro FCPXML 来工作。Final Cut 负责渲染。
工具位于 agent-motion/ 目录中:
| 工具 | 功能 |
|---|---|
motionkit.py inspect FILE [--json] | 图层树、文本层(文本/字体/大小/位置)、以秒为单位的时长、发布到 FCP 的内容、媒体引用 |
motionkit.py set-text IN OUT --set "KEY=TEXT" ... | 重写文本层。KEY 可以是图层 id、图层名称或当前文本。\n 表示换行。自动修复 styleRun 长度(UTF-16),确保 Motion 不会出错。 |
motionkit.py keyframes FILE [--grep X] [--all] | 每个动画参数及其关键帧时间/值(旋转使用弧度制) |
motionkit.py preview FILE --at 1.9 3.2 [--open] | 在指定时间点写入副本;--open 会在后台(open -g)在 Motion 中打开,用于 app_screenshot 截图 |
motionkit.py install FILE --name NAME | 将项目复制到 Motion 的“标题”文件夹中,作为 FCP 标题模板 |
titles_fcpxml.py SPEC.json -o OUT.fcpxml | 提示列表 → 生成 FCPXML 项目,包含正确时间点和填充好文本的标题 |
播客模板
模板安装在 ~/Library/Containers/com.apple.motionapp/Data/Movies/Motion Templates.localized/Titles.localized/Templates/ 目录下
(FCP uid 格式:~/Titles.localized/Templates/<Name>/<Name>.moti)。两个主要模板的快照文件位于 agent-motion/*.motn。
- TF_Podcast_title:主标题卡,5 秒,29.97 fps,1080p。文本层:
Tiersias Foundation Podcast(显示为“Tiresias Foundation Podcast”;图层名称有拼写错误,请继续使用该名称作为 key)和Episode 4: Carl Schmitt(每集可变的文本,Futura Medium 字体)。它发布了一个“拖放区”。 - Citation v2:引文和来源的下方三分之一字幕,PT Serif 字体。文本层:
~Author Lastname(斜体;播客在作者名前加~前缀)、Person Writes Silly Headline to(作品标题,可能有两行)、20 Apr, 1969(日期)、Publication(出版物来源)。 - Citation v2 no pub:与上相同,但没有出版物来源行。此外还有 Citation v1、Black/Orange/Yellow/PeeTitle、Minister_Types 等模板。
以往使用情况(来自 Podcast3/4 资源库):Timothy Snyder、Carl Schmitt、Xunzi、
~Eric L. Hutton, trans.、~Armando Iannucci, dir.、95 U.S. 602。译者或导演会在名称后添加角色后缀。法庭案例放在作者位置。
工作流程
- 运行
python3 agent-motion/motionkit.py inspect "<template>.moti"获取文本层的 key。 - 编写 cue JSON 文件(参见
agent-motion/examples/ep_demo.json):指定模板名称、时间点(at,以秒为单位,指在节目时间线上的位置)、可选时长(duration)以及文本映射(texts)。 - 运行
python3 agent-motion/titles_fcpxml.py cues.json -o titles.fcpxml,然后验证:xmllint --noout --dtdvalid <FCPXMLv1_11.dtd 副本的路径> titles.fcpxml。DTD 文件位于/Applications/Final Cut Pro.app/Contents/Frameworks/Interchange.framework/Versions/A/Resources/。 需要先复制一份 DTD,因为 xmllint 在包含空格的路径上会失败。 - 推送到 FCP:添加
--library <bundle> --push参数(零点击操作)。如果不加--library,FCP 会弹出资源库选择窗口,因此将此选择留给用户。生成的标题会连接在一个间隙上方,方便用户将其复制到实际编辑时间线上。
对于一次性生成的静态卡片(例如缩略图或 FCP 无法覆盖样式更改的卡片),可以使用 set-text 命令修改 .motn 文件,并用 open -g -a Motion OUT.motn 命令打开给用户预览或导出。
在 FCP 11.0.1 中验证(2026-10-18)
- 零点击导入工作正常:
titles_fcpxml.py cues.json -o x.fcpxml --library path/To.fcpbundle --push会添加<import-options>(库位置、链接资源、抑制警告),并通过open -g发送。FCP 会在后台导入,如果库不存在则自动创建。测试时请使用scratch/fcp-test/下的临时库,切勿未经询问操作用户的真实资源库。 <text>的顺序 = Motion 文档中文本层的顺序。 已验证:Citation v2 的作者、标题、日期和出版物都出现在正确的位置。- 发送纯文本(默认的
--style none)。 这样模板会保留自己的字体、大小、颜色和阴影。--style font会发送一个包含字族/字形/大小的text-style-def。任何样式定义都会覆盖模板,并且没有Size参数的图层(Motion 默认 48)在添加 48 回退之前会变得非常小。仅在需要故意重设样式时才使用--style font。 - 主标题卡(TF_Podcast_title)导入时动画保持完整。
- 通过
com.apple.FinalCut的app_screenshot读取 FCP 状态:点击事件,然后点击浏览器中的项目图标(双击名称会启动重命名,因此请按 Escape 键退出),再点击时间线标尺。
查看与渲染(2026-10-18 验证)
- 预览帧: 运行
motionkit.py preview FILE --at T --open,然后通过计算机操作在 Motion(bundle idcom.apple.motionapp)上执行app_screenshot,再放大画布。通过app_menu File > Close关闭窗口。从后台向 Motion 的播放头字段输入内容不会移动播放头。<currentFrame>副本是可靠的操作方式。 - 渲染: 计算机操作无法点击 File > Share(导出影片、存储当前帧)。请让用户手动点击导出。Shell 的
screencapture缺少屏幕录制权限。 - 切勿不加
-g参数使用open -a。用户正在同一个屏幕上工作。
动画(XML 中暴露的内容)
关键帧是 curve > keypoint{time, value, in/out tangents},位于任何参数下(变换位置/旋转/缩放、混合不透明度、滤镜参数)。行为(如 Gather In、Pen Speed/Pressure)是它们自己的节点,包含时间和控制参数。因此我们可以读取完整的动画编排,并以编程方式重新调整时间、偏移或添加关键帧。然后通过预览和截图来检查。
TF_Podcast_title 的动画编排:Tiresias 人物(Group 1/2)从 ±400 px 滑入到 1.5 秒位置,然后在 ±10 px / ±2° 范围内摇摆到 4.87 秒。蛇形图像对在 1.74 秒弹出(缩放 0.17→0.62,持续到 2.1 秒,由圆角矩形遮罩),并在 2.1/3.17/4.14/4.87 秒的关键帧之间向前猛冲。标题文本在 1.5–1.7 秒淡入,并带有从 1.0 秒开始的 Gather In 效果,而剧集文本在 2.37 秒开始 Gather In。
ozml 格式说明
- 时间表示为
"value timescale flags epoch";秒数 = value/timescale。 - 文本内容:
scenenode[factory=Text] > parameter[name=Object] > parameter[id=369] > text。 样式通过styleRun style=ID offset length定义,因此长度不匹配会破坏样式。 - 文件顶部的 factories 映射将
factoryID映射到类型(Text、Shape、Replicator、Image...)。这些 id 在每个文件中都不同,因此必须始终通过 factory 表来解析。 - Apple 的(旧版,但大部分仍然正确)参考文档:developer.apple.com 存档中的 Motion XML 指南。