关于 moviepy
面向 moviepy 视频制作的技能,用于给 AI 生成的画面添加准确、可控的文字,也可构建短篇 Python 视频项目。适合需要可靠标签和字幕且不使用 Remotion 工具链的编辑者。
主要功能
- 01使用 PIL 添加确定性文字叠层
- 02合成视频与数据驱动视觉内容
- 03以音频为基准的场景时间线
- 04混合旁白并降低背景音乐音量
- 05可运行的短视频示例
适用场景
- 01为 AI 生成画面添加准确名称和字幕
- 02用 Python 制作短广告视频
- 03为图表叠加标题和来源标注
- 04按场景旁白时长对齐视频
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ digitalsamba/ claude-code-video-toolkit --skill moviepy安装后在 Claude Code 中输入/moviepy确认已加载。
详细介绍
用于视频制作的 moviepy
moviepy 是工具包中用于在 AI 生成的视频上添加确定性的文字,以及在不使用 Remotion 工具链的情况下构建短小、单文件 Python 视频项目的首选库。
更深层的原则是可信赖的文字:凡是文字必须清晰可读、准确且一致的类型(无论是在法律、编辑还是商业方面),都不能接受由 AI 在画面内生成文字,而使用 moviepy 叠加文字是自然的解决方案。姓名必须拼写正确。价格必须准确。来源署名必须做到像素级精准。AI 生成模型无法保证这些。
何时使用 moviepy,何时使用 Remotion
| 适合使用 moviepy 的场景…… | 适合使用 Remotion 的场景…… |
|---|---|
| 在 LTX-2 或 SadTalker 的输出上叠加文字/标签 | 构建长篇冲刺回顾或产品演示 |
在单个 build.py 中制作 30 秒以内的广告风格短片 | 多模板、多品牌、设计要求较高的工作 |
合成数据驱动的视觉效果(matplotlib FuncAnimation → mp4) | 任何需要 React 组件或复用设计系统的场景 |
| 对现有视频文件进行一次性转换 | 任何需要关注项目生命周期(规划 → 渲染)的场景 |
| 希望完全避免 Node.js/React 带来的心智负担 | 希望在 Remotion Studio 中热重载预览 |
本技能的 examples/ 中提供了两个可运行的参考示例:
examples/quick-spot/build.py— 15 秒广告风格短片。以音频为锚点的时间线、文字叠加、可选画外音 + 自动降低背景音乐音量。无需外部素材,开箱即可渲染无声视频。examples/data-viz-chart/build.py— 带有确定性标题和来源署名的动态时间序列图表。展示 matplotlib(数据)+ moviepy(可信赖的文字)的分工。
两个示例都可通过 uv run build.py 运行,并立即生成真正的 out.mp4。请结合本技能阅读这两个示例——下文介绍的每种模式都能在那里找到可运行的实现。
依赖项。 moviepy、Pillow 和 matplotlib 已在根目录的 pyproject.toml 中声明,并通过工具包的一行式 Python 安装命令安装:uv sync。如果运行示例时遇到 Missing dependency,请在仓库根目录运行该命令——示例中的 build.py 文件也会在错误消息中提示同一操作,并正常退出,而不是只打印一段回溯信息。
主要用例:在 AI 生成的视频上添加文字
LTX-2 和 SadTalker 输出的都只是纯画面:
- LTX-2 无法可靠地渲染可读文字(模型会幻觉出字形——请参阅 ltx2 技能中的“Bad Prompts”)。
- SadTalker 输出的是没有字幕、标签、下三分之一字幕条或背景信息的说话人头部视频。
解决方法是先干净地生成画面,再用 moviepy 以确定性的方式在其上合成文字。这是本工具包中的典型模式:
from moviepy import VideoFileClip, ImageClip, CompositeVideoClip
# 1. AI-generated visual (LTX-2 or SadTalker output)
bg = VideoFileClip("lugh_ltx.mp4").without_audio()
# 2. Text rendered via PIL → ImageClip (see "Text rendering" below)
title = (
ImageClip("text_cache/intro_title.png")
.with_duration(2.0)
.with_start(0.5)
.with_position(("center", 880))
)
# 3. Composite
final = CompositeVideoClip([bg, title], size=(1920, 1080))
final.write_videofile("lugh_with_caption.mp4", fps=30, codec="libx264")
常见形式如下:
| 形式 | LTX-2 用法 | SadTalker 用法 |
|---|---|---|
| 主视觉画面上的标题卡 | 在电影感的 LTX-2 辅助镜头上叠加“INTRODUCING LONGARM” | 不适用 |
| 下三分之一字幕条/姓名牌 | 不适用 | 在说话人头部视频下方显示“Lugh — Ancient Warrior God” |
| 引语字幕 | 在 LTX-2 人物客串画面上叠加“I am going home.” | 同样适用,叠加在 SadTalker 说话人头部视频上 |
| 品牌署名 | 在最后一秒淡入徽标 + URL | 同样适用 |
| 用于增强对比度的色调叠加层 | 在文字后添加半透明深海军蓝图层 | 同样适用 |
适用的类型
“AI 视觉 + 确定性文字叠加”模式是多种视频风格的自然制作流程。如果需求符合以下某种类型,默认选择 moviepy:
| 类型 | 叠加内容 | moviepy 是合适选择的原因 |
|---|---|---|
| 新闻 / 访谈新闻 | 发言人姓名牌、地点栏、突发新闻横幅、来源署名、引用语 | 姓名必须拼写正确(编辑 / 法律要求)。按数量计算,这是最大的类别。 |
| 纪录片片段 | 受访者下三分之一字幕、章节标题、档案来源署名、地点标记 | 与新闻一样,必须确保可信度。 |
| 预告片 / 宣传短片 | 标题卡、演职员表叠加(“导演作品……”)、日期特效、引用卡、行动号召 | 时间点要求精准、文字内容多,每一帧都很重要。q2-townhall-longarm-ad 示例就是这种情况。 |
| 社交媒体短视频(Reels、TikTok、Shorts) | 可在静音播放时准确呈现的字幕、标签叠加 | 大多数社交媒体视频都是静音播放;字幕必不可少。 |
| 带标注的产品演示 | 价格提示、功能标签、屏幕录制上的“点击此处”指示、前后对比标签 | 价格和产品名称必须准确无误。 |
| 教程 / 讲解视频 | 步骤编号叠加、终端命令字幕、键盘快捷键提示 | 步骤编号必须连续,命令必须可直接复制粘贴。 |
也适用但较少见的类型:音乐视频(歌词叠加)、反应视频(来源署名)、体育赛事集锦(比分叠加)、房地产导览(价格 / 平方英尺)、会议演讲(发言人 + 会议环节信息牌)。
对于完整的 SRT 字幕(长视频、带时间码、多语言),moviepy 可以胜任,但并非理想选择——建议使用带 subtitles 滤镜的 ffmpeg,或专用字幕工具。moviepy 最适合手动放置叠加内容,而非批量处理字幕轨。
文字渲染——使用 PIL,而非 TextClip
**关键问题:**moviepy 2.x 的 TextClip(method='label') 存在紧框边界框(tight-bbox)错误,会裁掉字母的上伸部和下伸部(大写字母的顶部,以及 g/p/y 的尾部)。在 Apple Silicon 上,你会看到字符边缘被切掉,却可能花上好几个小时都没发现问题所在。
**解决方法:**通过 PIL 将文字渲染为透明 PNG,然后将其加载为 ImageClip。按内容哈希缓存结果,这样重新构建就无需额外开销。
import hashlib
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
ARIAL_BOLD = "/System/Library/Fonts/Supplemental/Arial Bold.ttf"
def render_text_png(txt, size, hex_color, cache_dir="./text_cache"):
cache = Path(cache_dir); cache.mkdir(parents=True, exist_ok=True)
key = hashlib.sha1(f"{txt}|{size}|{hex_color}".encode()).hexdigest()[:16]
path = cache / f"{key}.png"
if path.exists():
return str(path)
font = ImageFont.truetype(ARIAL_BOLD, size)
bbox = ImageDraw.Draw(Image.new("RGBA", (1, 1))).textbbox((0, 0), txt, font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
pad = max(20, size // 4)
img = Image.new("RGBA", (tw + pad * 2, th + pad * 2), (0, 0, 0, 0))
rgb = tuple(int(hex_color.lstrip("#")[i:i+2], 16) for i in (0, 2, 4))
ImageDraw.Draw(img).text((pad - bbox[0], pad - bbox[1]), txt, font=font, fill=(*rgb, 255))
img.save(path)
return str(path)
完整辅助函数(含粗体、位置、淡入淡出等关键字参数,使用体验也更好)位于 examples/quick-spot/build.py——直接复制使用,不要重新实现。
以音频为锚点的时间线模式
对于每一帧都很重要的广告风格剪辑,先生成每个场景的 VO,再将所有画面锚定到已知的绝对时间戳。这样可以彻底消除时间漂移。完整模式请参阅 CLAUDE.md → Video Timing → Audio-Anchored Timelines。简要版如下:
# Audio-anchored timeline (25s):
# Scene 1 tired 0.3 → 3.74 (audio 3.44s)
# Scene 2 worries 4.0 → 8.88 (audio 4.88s)
text_clip("TIRED OF", start=0.5, duration=1.2)
text_clip("THIRD-PARTY", start=1.0, duration=1.8)
vo_clip("01_tired.mp3", start=0.3)
vo_clip("02_worries.mp3", start=4.0)
常用配方
在单个 AI 生成的视频片段上添加文字
from moviepy import VideoFileClip, ImageClip, CompositeVideoClip
bg = VideoFileClip("ltx_hero.mp4").without_audio()
caption = (
ImageClip(render_text_png("THE FUTURE OF AGENTS", 140, "#FFFFFF"))
.with_duration(bg.duration)
.with_position(("center", 880))
)
CompositeVideoClip([bg, caption], size=bg.size).write_videofile("captioned.mp4", fps=30)
在 SadTalker 访谈画面上添加下三分之一字幕
from moviepy import VideoFileClip, ImageClip, ColorClip, CompositeVideoClip
talking = VideoFileClip("narrator_sadtalker.mp4")
W, H = talking.size
# Semi-transparent bar across the bottom for contrast
bar = (
ColorClip((W, 140), color=(20, 24, 38))
.with_duration(talking.duration)
.with_opacity(0.75)
.with_position(("center", H - 160))
)
name = (
ImageClip(render_text_png("LUGH", 72, "#F06859"))
.with_duration(talking.duration)
.with_position((80, H - 150))
)
title = (
ImageClip(render_text_png("Ancient Warrior God", 36, "#FFFFFF"))
.with_duration(talking.duration)
.with_position((80, H - 80))
)
CompositeVideoClip([talking, bar, name, title]).write_videofile("with_lower_third.mp4", fps=30)
为繁杂画面上的文字添加有色叠加层,增强对比度
LTX-2 的 b-roll 画面往往过于繁杂,文字难以辨认。在视频和文字之间添加半透明的藏青色图层:
from moviepy import ColorClip
tint = (
ColorClip((W, H), color=(20, 24, 38))
.with_duration(duration)
.with_opacity(0.55)
)
# Composite order: bg → tint → text
CompositeVideoClip([bg, tint, text_clip])
并排合成
from moviepy import VideoFileClip, CompositeVideoClip, ColorClip
left = VideoFileClip("demo_a.mp4").resized(width=960).with_position(( 0, "center"))
right = VideoFileClip("demo_b.mp4").resized(width=960).with_position((960, "center"))
bg = ColorClip((1920, 1080), color=(0, 0, 0)).with_duration(max(left.duration, right.duration))
CompositeVideoClip([bg, left, right]).write_videofile("split.mp4", fps=30)
混合每个场景的 VO 与自动降低音量的音乐
from moviepy import AudioFileClip, CompositeAudioClip
from moviepy.audio.fx.MultiplyVolume import MultiplyVolume
from moviepy.audio.fx.AudioFadeIn import AudioFadeIn
from moviepy.audio.fx.AudioFadeOut import AudioFadeOut
music = AudioFileClip("music.mp3").with_effects([
MultiplyVolume(0.22), # duck under VO
AudioFadeIn(0.5),
AudioFadeOut(1.5),
])
vo = [
AudioFileClip(f"scenes/0{i}.mp3").with_effects([MultiplyVolume(1.15)]).with_start(start)
for i, start in [(1, 0.3), (2, 4.0), (3, 9.1)]
]
final_audio = CompositeAudioClip([music] + vo)
注意事项
- moviepy 2.x 重命名了方法。 使用
subclipped(而不是subclip),使用with_duration/with_start/with_position(而不是set_duration等),使用with_effects([...])代替.fadein()/.fadeout()。网上的许多教程仍使用 1.x 语法,请谨慎参考。 TextClip(method='label')会裁掉字母的上伸部和下伸部。 请始终使用上文介绍的 PIL 变通方法。OffthreadVideo仅适用于 Remotion。 moviepy 使用VideoFileClip。不要混用两者。- 调整大小需要 Pillow ≥ 10.0 才能使用 LANCZOS 重采样。如果出现
ANTIALIAS错误,请升级 Pillow。 ColorClip接受 RGB 元组,而不是十六进制字符串。 使用(20, 24, 38),不要使用"#141826"。VideoFileClip默认会加载音频。 如果只需要画面,请调用.without_audio()——将不需要的音频合成进去,会导致CompositeAudioClip中的静音 VO 丢失。- 始终为
CompositeVideoClip设置size=(W, H)。 否则,输出尺寸将取决于第一个片段,可能小于目标尺寸。
何时使用哪种工具
| 任务 | 工具 |
|---|---|
| 为静态图像添加动画 | tools/ltx2.py --input |
| 根据写实肖像生成会说话的人物视频 | tools/sadtalker.py |
| 根据风格化角色生成会说话的人物视频 | tools/ltx2.py --input(参见 ltx2 技能) |
| 为以上任一视频添加标签/字幕/下三分之一字幕条 | moviepy + PIL(此技能) |
| 转换/压缩/调整现有文件的大小 | ffmpeg(参见 ffmpeg 技能) |
| 制作长篇、由设计系统驱动的视频 | Remotion(参见 remotion 技能) |
参考资料
- 可运行示例——短篇广告风格视频:
examples/quick-spot/build.py - 可运行示例——带文字叠加的数据可视化:
examples/data-viz-chart/build.py - 以音频为锚点的时间线:
CLAUDE.md → Video Timing → Audio-Anchored Timelines - 相关技能:
ltx2、ffmpeg、remotion