01

一键视频的秘密

一个主题词,30 秒后 AI 帮你写剧本、找素材、配音、加字幕、合成视频——这就是 MoneyPrinterTurbo

想象你是一位短视频创作者

你只需要输入一个主题——比如"春天的花海"——然后按下一个按钮。接下来发生的事情就像一个全自动工厂流水线,每个车间各司其职,最终送出一段带配音、带字幕、带背景音乐的高清短视频。

💡
核心思想

MoneyPrinterTurbo 把"做视频"这件事拆成了 7 道工序。就像快递公司分拣、打包、运输一样,每道工序只做一件事,但串起来就能完成从"想法"到"成品"的全部工作。

代码仓库长什么样

项目根目录下的每个文件夹就像工厂里的一个车间。点击下方的架构图,了解每个"车间"负责什么。

app/ 核心业务逻辑
config/ 读取 config.toml 配置文件
controllers/ 接收 HTTP 请求,触发任务
models/ 定义数据结构(视频参数、请求/响应格式)
services/ 7 道工序的具体实现
llm.py调用 AI 大模型生成剧本和搜索词
voice.py文本转语音(TTS)
material.py从图库搜索并下载视频素材
video.py拼接、加字幕、加配乐、输出最终视频
subtitle.py用 Whisper AI 生成字幕
task.py编排全部工序的总调度器
upload_post.py自动发布到社交平台
webui/ Streamlit 可视化操作界面
main.py启动 Web 服务器的入口文件
config.toml你的 API 密钥、模型选择等配置

七步流水线:从想法到视频

当你输入主题后,task.py 会像指挥家一样按顺序调用七个工序。每完成一步,进度条就往前走一点。

1

生成剧本

2

提取搜索词

3

文本转语音

4

生成字幕

5

搜索下载素材

6

拼接视频片段

7

叠加字幕配乐

CODE

def start(task_id, params: VideoParams, stop_at="video"):
    # 1. 用 AI 生成剧本
    video_script = generate_script(task_id, params)
    # 2. 用 AI 提取视频搜索关键词
    video_terms = generate_terms(task_id, params, video_script)
    # 3. 把剧本变成语音(TTS)
    audio_file, audio_duration, sub_maker = generate_audio(task_id, params, video_script)
    # 4. 根据语音生成字幕
    subtitle_path = generate_subtitle(task_id, params, video_script, sub_maker, audio_file)
    # 5. 根据关键词从图库下载素材
    downloaded_videos = get_video_materials(task_id, params, video_terms, audio_duration)
    # 6-7. 拼接片段 + 叠加字幕配乐
    final_paths, combined_paths = generate_final_videos(task_id, params, downloaded_videos, audio_file, subtitle_path)
          
代码旁注

这是总调度函数 start(),接收任务 ID 和视频参数

第一步:让 AI 根据主题写出视频旁白稿

第二步:让 AI 提取用于搜索视频素材的英文关键词

第三步:把旁白稿转成语音文件,同时获得音频时长

第四步:根据语音的时间轴生成精确的字幕

第五步:用搜索词去 Pexels/Pixabay 找并下载视频片段

第六、七步:把所有片段拼在一起,加上字幕和背景音乐

最终返回成品的文件路径

数据在七道工序间的流动

点击"下一步"按钮,看看用户输入的主题"春天的花海"是如何一步步变成最终视频的。

👤
用户
🤖
AI
🎤
TTS
📄
字幕
🎥
素材库
🎬
合成
🌟
成品
点击"下一步"开始动画

一份数据贯穿全程

所有工序共享同一个 VideoParams 参数对象。它就像一份详细的生产订单,上面写满了你对视频的所有要求。

🎬

画面设定

video_subject(主题)、video_aspect(9:16/16:9/1:1)、video_concat_mode(随机/顺序拼接)

🎤

声音设定

voice_name(200+ 种人声可选)、voice_rate(语速)、bgm_type(背景音乐)、bgm_volume(音量)

📄

字幕样式

font_name(字体)、font_size(大小)、text_fore_color(文字颜色)、subtitle_position(位置)

📷

素材来源

video_source(pexels/pixabay/本地)、video_materials(自定义素材列表)、video_clip_duration(片段时长)

理解检测

如果你要修改视频生成的工序顺序,应该改哪个文件?

项目中的进度追踪(进度条百分比)是在哪里更新的?

02

AI 剧本生成

大模型如何理解你的主题,写出旁白稿,并提取出精准的视频搜索词

先写稿,再想画面

LLM 服务(llm.py)做了两件关键的事。就像一位编剧:先根据你的主题写出完整的旁白稿,然后从稿子里提炼出英文关键词,用来在图库里搜索对应的视频画面。

generate_script() —— AI 编剧

接收主题(如"春天的花海"),用 Prompt 让大模型写出适合短视频的旁白稿

🔍
generate_terms() —— AI 搜索词专家

接收剧本和主题,让大模型生成 5 个英文搜索词,供后续在 Pexels/Pixabay 搜索视频素材

Prompt:给 AI 的创作简报

generate_script() 构造了一段精心设计的 Prompt,就像给编剧一份创作简报。

CODE

def generate_script(video_subject, language="", paragraph_number=1):
    prompt = f"""
    # Role: Video Script Generator
    ## Goals:
    Generate a script for a video, depending on
    the subject of the video.
    ## Constrains:
    1. return as a string with specified paragraphs.
    2. do not reference this prompt.
    3. get straight to the point.
    5. only return raw content of the script.
    8. respond in the same language as subject.
    # Initialization:
    - video subject: {video_subject}
    - number of paragraphs: {paragraph_number}
    """.strip()
          
代码旁注

函数接收三个参数:视频主题、语言、段落数

用三引号构造多行 Prompt(发给 AI 的指令)

设定角色身份:视频剧本生成器

明确目标:根据主题生成视频剧本

添加约束条件,让输出更可控

不要在回答中提到这个 Prompt 本身

直入主题,不要说"欢迎观看本期视频"

只返回纯文本,不加 Markdown 格式

用与主题相同的语言回答

注入变量:实际的视频主题和段落数

f-string 让 {video_subject} 变成实际的主题词

16 家 AI 模型的大联盟

llm.py 的 _generate_response() 函数支持 16 种 LLM Provider。来看看 AI 模型之间如何"对话"。

从中文剧本到英文搜索词

generate_terms() 同样用 Prompt 让 AI 从剧本中提炼出 JSON 格式的英文搜索词。为什么必须是英文?因为 Pexels 和 Pixabay 是海外图库,英文搜索效果最好。

CODE

def generate_terms(video_subject, video_script, amount=5):
    prompt = f"""
    # Role: Video Search Terms Generator
    ## Constrains:
    1. return as a json-array of strings.
    5. reply with english search terms only.
    ## Output Example:
    ["search term 1", "search term 2"]
    """.strip()
    search_terms = json.loads(response)
          
代码旁注

接收主题、剧本和需要的关键词数量(默认 5 个)

Prompt 设定角色为视频搜索词生成器

约束:返回 JSON 数组格式,如 ["sky", "tree"]

关键约束:必须返回英文搜索词

给出输出示例,让 AI 知道期望格式

AI 返回的字符串直接用 json.loads 解析成列表

理解检测

为什么 generate_terms() 要求 AI 只返回英文搜索词?

如果 AI 返回了带 Markdown 格式的剧本(如 **加粗** 或 ## 标题),会发生什么?

03

素材匹配引擎

从语音合成到字幕生成,从图库搜索到智能下载——三道工序如何配合

让文字开口说话

voice.py 是项目的"配音演员"。它接收剧本文本,调用 TTS 服务生成语音文件,同时返回一个精确到每个词的时间轴,供后续生成字幕使用。

🎤

Edge TTS(免费)

微软 Edge 浏览器的免费语音服务。200+ 种声音,支持中文"晓晓""云希"等。返回逐词时间戳

Azure Speech V2

微软付费语音服务。音质更好,支持多语言混合语音,返回更精确的词级边界信息

🤖

SiliconFlow / Gemini

第三方 AI 语音服务。Gemini TTS 提供 15 种声音如 Zephyr、Puck。拿不到逐词边界时会按字符比例分配时间

TTS 调度:一个函数适配四种服务

tts() 函数像一位前台接待员——看看来访者是哪里的,就转接给对应的分机号。

CODE

def tts(text, voice_name, voice_rate, voice_file, voice_volume=1.0):
    if is_azure_v2_voice(voice_name):
        return azure_tts_v2(text, voice_name, voice_file)
    elif is_siliconflow_voice(voice_name):
        return siliconflow_tts(text, model, full_voice, voice_rate, voice_file)
    elif is_gemini_voice(voice_name):
        return gemini_tts(text, voice, voice_rate, voice_file)
    return azure_tts_v1(text, voice_name, voice_rate, voice_file)
          
代码旁注

统一入口函数,接收文本、声音名、语速、输出路径

如果声音名以 -V2 结尾,走 Azure 付费版

如果声音名以 siliconflow: 开头,走硅基流动

如果声音名以 gemini: 开头,走 Gemini TTS

默认走免费的 Edge TTS(azure_tts_v1)

两种字幕生成策略

subtitle.py 有两条字幕生成路线。就像翻译有两种方式——一种对照原文逐句翻,另一种先听录音再翻。

1
Edge TTS 时间轴(优先)

Edge TTS 返回 SubMaker 对象,包含每个词的精确时间戳。voice.py 中的 create_subtitle() 把逐词时间轴聚合成按句分段的 SRT 字幕

2
Whisper AI 语音识别(备用)

如果 Edge 路线失败,subtitle.py 会调用 Whisper 模型对音频做语音识别,然后与原剧本用"编辑距离"算法校对修正

🔎
编辑距离校对

subtitle.correct() 函数用 Levenshtein 距离算法比较 AI 识别的字幕与原始剧本,当相似度超过 80% 就自动合并修正——就像一位校对编辑对照原稿修改错别字。

从图库找画面

material.py 是项目的"采购员"——拿着搜索词去 Pexels 或 Pixabay 找合适的视频片段,下载到本地。它甚至会轮换 API Key 来避免被限速。

🔍
搜索词
🌐
Pexels
📋
筛选
时长检查
💾
本地文件
点击"下一步"开始动画

将组件与职责配对

把左侧的组件名拖到右侧对应的职责描述上。

Edge TTS
Whisper
material.py
voice.tts()

免费的微软语音服务,返回逐词时间戳

拖到这里

OpenAI 开源语音识别模型,用于生成备选字幕

拖到这里

从 Pexels/Pixabay 搜索并下载视频片段

拖到这里

根据声音名称前缀自动选择对应的 TTS 服务

拖到这里

理解检测

当你选择声音 "gemini:Zephyr-Female" 时,代码是怎么知道该用 Gemini TTS 的?

如果 Pexels 限速了(请求太频繁被拒绝),代码里有什么应对机制?

04

视频合成流水线

拼接片段、添加转场、叠加字幕、混入配乐——把零散素材变成完整视频

先拼后合:两阶段视频加工

video.py 把合成分成了两个阶段。就像盖房子:先搭骨架(拼接),再搞装修(加字幕配乐)。

1
combine_videos() —— 骨架拼接

把下载的视频片段按顺序或随机排列,切成不超过 max_clip_duration 秒的小段,用 FFmpeg 串联成一段无声视频

2
generate_video() —— 精装修

在拼接好的视频上叠加字幕、混入配音和背景音乐,输出最终的 MP4 文件

智能拼接:像拼图一样对齐

每个下载的视频片段尺寸和比例可能不同。combine_videos() 像一个智能拼图工人——把不合适的裁掉多余部分,实在裁不了的就在周围加黑边补齐。

CODE

# 如果比例不同,等比缩放后加黑边
if clip_ratio > video_ratio:
    scale_factor = video_width / clip_w
else:
    scale_factor = video_height / clip_h
new_width = int(clip_w * scale_factor)
new_height = int(clip_h * scale_factor)
background = ColorClip(
    size=(video_width, video_height),
    color=(0, 0, 0)
).with_duration(clip_duration)
clip_resized = clip.resized(
    new_size=(new_width, new_height)
).with_position("center")
clip = CompositeVideoClip([background, clip_resized])
          
代码旁注

比例不同时,按短边等比缩放,确保画面不变形

如果原视频更宽,按宽度缩放

如果原视频更高,按高度缩放

计算缩放后的新尺寸

创建一个黑色背景画布,尺寸等于目标分辨率

把缩放后的视频放在画布正中央

最终的复合视频 = 黑色背景 + 居中片段

五种转场效果

拼接视频片段时,可以选择在片段之间添加转场动画,让过渡更自然。video_effects.py 提供了五种效果。

🎬

None

无转场,片段之间直接切换。最简单、渲染最快

🎨

FadeIn / FadeOut

淡入:从黑屏逐渐变清晰。淡出:从清晰逐渐变黑。经典影视效果

SlideIn / SlideOut

从左/右/上/下方向滑入或滑出。每次随机选方向

🔀

Shuffle

随机混合以上四种效果,每个片段可能用不同的转场

字幕渲染:精确到像素

generate_video() 中的 create_text_clip() 函数负责在视频上渲染字幕。它会自动换行、自动计算大小,并支持四种位置。

CODE

_clip = TextClip(
    text=wrapped_txt,
    font=font_path,
    font_size=params.font_size,
    color=params.text_fore_color,
    bg_color=resolve_subtitle_background_color(),
    stroke_color=params.stroke_color,
    stroke_width=params.stroke_width,
    text_align="center",
)
if params.subtitle_position == "bottom":
    _clip = _clip.with_position(
        ("center", video_height * 0.95 - _clip.h)
    )
          
代码旁注

用 MoviePy 的 TextClip 创建字幕文字层

wrap_text() 处理过的自动换行文本

字体文件路径(如 STHeitiMedium.ttc 黑体)

前景色(白色 #FFFFFF)

背景色(默认黑色 #000000 或透明)

描边颜色和宽度,让文字更清晰

文字居中对齐

如果位置是"底部",精确计算 Y 坐标

放在屏幕底部 95% 的位置,留一点底部间距

找找看:这段代码有什么问题?

下面是 combine_videos() 中的一段真实代码,有一个可能导致画面比例变形的隐患。点击你认为有问题的行。

1 clip_ratio = clip.w / clip.h
2 video_ratio = video_width / video_height
3 if clip_ratio == video_ratio:
4 clip = clip.resized(new_size=(video_width, video_height))
5 else:
6 background = ColorClip(size=(video_width, video_height))

理解检测

视频合成为什么要分两个阶段(combine + generate)?

如果下载的视频素材总时长比音频短,会怎样?

05

发布与扩展

API 接口设计、任务管理系统、跨平台发布,以及如何基于此项目做二次开发

RESTful API:像点外卖一样下单

controllers/v1/video.py 定义了 RESTful API 接口。用户就像在点外卖——POST 一个请求下单,GET 一个请求查看进度。

POST /v1/videos 提交视频生成任务(像下单),返回 task_id
POST /v1/subtitle 只生成字幕(不生成视频),可单独调试
POST /v1/audio 只生成音频(不生成视频),可单独调试
GET /v1/tasks/{task_id} 查询任务状态和进度(像查外卖到哪了)
GET /v1/tasks 获取所有任务列表(分页)
DELETE /v1/tasks/{task_id} 删除任务及生成的视频文件
GET /v1/stream/{path} 流式播放视频(支持 Range 断点续传)
GET /v1/download/{path} 下载视频文件

任务调度:后厨的排号系统

视频生成是耗时操作(通常 1-3 分钟),不能让用户一直等待响应。项目采用了 异步任务 模式。

CODE

if _enable_redis:
    task_manager = RedisTaskManager(
        max_concurrent_tasks=_max_concurrent_tasks,
        redis_url=redis_url
    )
else:
    task_manager = InMemoryTaskManager(
        max_concurrent_tasks=_max_concurrent_tasks
    )
task_manager.add_task(tm.start, task_id, params, stop_at)
          
代码旁注

如果配置了 Redis(内存数据库),用分布式任务管理器

Redis 模式:多个进程/机器可以共享任务队列

否则用内存模式,适合单机运行

两种模式接口一致,通过配置切换无需改代码

add_task() 把任务放入队列,立即返回 task_id

后台线程从队列取出任务执行 tm.start()

💡
策略模式

InMemoryTaskManager 和 RedisTaskManager 实现了相同的接口——这就是设计模式中的"策略模式"。你的代码只跟"任务管理器"打交道,根本不需要知道底层用的是内存还是 Redis。

自动发布到社交平台

upload_post.py 提供了自动发布功能。视频生成后,如果配置了发布凭据,它会自动把视频发到 TikTok / Instagram 等平台。

🎬
视频生成
配置检查
🌐
社交平台
📋
发布结果
点击"下一步"开始动画

全系统架构总览

点击下方的任意组件,了解它在整个系统中的角色。

用户层

💻
WebUI
🔌
REST API

控制器层

🔁
Router
📅
Task Manager

服务层(七道工序)

🎯
Task Orchestration
🤖
LLM Service
🎤
TTS Service
📄
Subtitle Service
📷
Material Service
🎬
Video Service

外部服务

🌐
Pexels
🌐
Pixabay
🤖
LLM Providers
点击任意组件查看详细说明

如何基于此项目做二次开发

MoneyPrinterTurbo 的模块化设计让它非常适合二次开发。以下是几个常见的扩展方向。

接入新的 AI 模型

在 llm.py 的 _generate_response() 中添加新的 elif 分支,配置 API Key 和 Base URL 即可

🎤

接入新的 TTS 服务

在 voice.py 的 tts() 中添加新的声音前缀检测和服务调用函数,返回 SubMaker 对象

📷

接入新的素材源

在 material.py 中添加新的 search_videos_xxx() 函数,遵循统一的返回格式 List[MaterialInfo]

🌐

接入新的发布平台

在 upload_post.py 中扩展 cross_post_video(),添加新的平台适配器

理解检测

如果你想从单机部署升级到分布式部署(多台服务器),需要改哪些代码?

如果你只想调试"AI 生成剧本"这一步,不想等整个视频生成完成,有什么办法?

假设你想接入一个全新的 AI 模型(比如"通义千问"),代码改动范围有多大?