ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI短剧生成平台实战:从一句话到成片的四段式Pipeline部署指南

AI短剧生成平台实战:从一句话到成片的四段式Pipeline部署指南 简介这是一套基于 AI 的短剧生成平台完整源码与部署资料面向短视频创作者、AI 视频开发者和独立开发者。用户只需输入一句话即可自动完成剧本改写、角色与场景提取、分镜拆解、TTS 配音、文生视频/图生视频合成及整集导出形成从创意到成片的一站式工作流。资源共 94 个文件压缩包仅 634KB代码以 TypeScript56 个 ts为主配合 Vue 前端组件、Markdown 说明文档、JSON 配置及 Docker 编排文件覆盖后端接口、前端页面、配置示例与部署流程。内容包含角色管理、分镜制作、视频生成、素材库和任务追踪等核心模块并提供 Docker 编排、环境变量与依赖管理等工程化配置便于本地快速启动与二次开发整套工程结构清晰目录划分明确便于按模块检索。目前已有 305 人学习下载适合希望低成本搭建生成式短剧工具链、研究 AI 视频工作流或有完整项目参考需求的读者。1. 一句话生成一部成片这个 AI 短剧生成平台到底解决了什么做短视频的人都有这种体会一条一分钟的短剧剧本要写两小时配音剪辑又要三小时一天最多出两三条还经常卡在“下一条到底拍什么”。AI短剧生成平台这类工具圈里也叫 ai漫剧 平台就是冲着这个痛点来的——你输入一句“一个外卖员在雨夜救了一只猫”它自动完成剧本编写、角色与场景提取、分镜生成、配音、视频合成最后交出一条能直接发布的成片。适合两类人批量做短剧内容的运营工作室以及想快速验证 AI 视频流水线的开发者。下面按这类源码项目的常见结构把管线怎么拆、部署怎么做、代码怎么改、哪几个环节最容易翻车一次讲清楚。2. 先立住四段式管线剧本、提取、分镜与合成的选型理由在碰代码之前先把管线立住。整套平台的本质是一条“文本 → 结构化数据 → 素材 → 视频”的流水线任何一个环节输出格式不稳定后面全崩。架构设计的第一原则不是“哪个模型能力更强”而是“每一段的输出是否可预测”。阶段输入输出关键依赖剧本编写一句话结构化 JSON 剧本LLMJSON mode角色与场景提取剧本 JSON角色表 场景拍摄清单规则 字段映射分镜与配音拍摄清单镜头列表 音频轨TTS 引擎视频合成镜头素材 音频成片 MP4FFmpeg四段式的好处是边界清晰每段都能单独替换。今天换一个更强的 LLM不影响后面的合成逻辑明天把 edge-tts 换成 CosyVoice也只需要改一个适配函数。下面逐个说选型理由。2.1 剧本编写为什么非要结构化 JSON很多人第一次接触这类项目会以为剧本模块就是“让 AI 写个故事”。真正落地时你会发现直接让模型输出自然语言剧本后面根本接不住角色在哪句出现、场景怎么切换、谁说了台词全都要靠人再去猜。常见做法是把剧本定义成一份 JSON Schema。我一般让模型按这个结构返回title、genre、synopsis、characters、scenes。characters 里是名字、性格标签、声线scenes 里是地点、时间、氛围、剧情说明、对话列表每条对话带说话人和台词。理由有三个一是反序列化零成本python 里 json.loads 一次就能拿到 dict二是字段缺失能被立即检测到缺角色或缺场景直接重试一次不用人工介入三是后续的角色场景提取、分镜生成都能直接遍历这个结构分镜模块根本不关心故事读起来顺不顺它只认字段。选模型时有个关键参数要选支持 JSON mode 的模型并在请求里显式声明返回 JSON。如果模型输出的是 Markdown 表格或带注释的文本解析阶段会不停踩坑。还有一点要写进提示词——这是短剧不是小说总时长控制在 60 到 90 秒不超过 3 个场景每个场景 2 到 4 句台词。不限制的话模型会写出一个 10 分钟的长片后面的 TTS 和合成阶段直接被拖垮。2.2 角色与场景提取从剧本到拍摄清单的两次映射把“角色和场景的提取”单独拆成一个模块是这类平台和普通“AI 写故事”最大的分水岭。我习惯把这件事看成两次映射第一次映射由 LLM 完成把一句话变成结构化剧本第二次映射由代码完成把剧本 JSON 变成拍摄清单。拍摄清单里的每一项都要是后面两个模块能直接消费的东西。字段包括 scene_id、location、time、mood、characters_on_screen、visual_prompt、dialogue、duration。characters_on_screen 不能只写“出现角色名”要写清楚每个角色在这个场景里的外观锚点比如“穿黄色雨衣的外卖员”。原因在于文生图模型不具备上下文记忆角色一致性只能靠外观描述去维持。visual_prompt 是给文生图准备的英文提示词这块属于隐性工作量。国内大模型对中文支持还行但 SD 类开源模型对中文很弱所以需要用模板拼英文把风格后缀、画幅比例、镜头方式都写死在模板里。一个常见的误解是觉得角色提取是 NLP 命名实体识别任务想自己训一个模型。在这个平台里角色名和地点都来自我们让 LLM 输出的 JSON提取本质是字段映射加少量规则不要在没数据的地方硬造轮子。2.3 分镜、TTS 与合成三个容易被低估的工程环节分镜生成听着高级实现起来反而是最机械的把一个 scene 按对话条数拆成 1 到 3 个 shot每个 shot 标注景别和镜头运动。close-up 用在情绪转折medium shot 用在对话wide shot 用在场景建立。这些词要拼进 visual_prompt因为画面内容会跟着景别变。TTS 的选型直接影响部署成本。免费且够用的方案是 edge-tts不需要下载模型文件联网就能用支持中文音色短剧对白完全够。如果想离线部署可以换 CosyVoice但需要准备几 GB 权重和至少 6 GB 显存。这里有个容易忽略的细节短剧台词语速比正常朗读快TTS 参数里 rate 一般要加到 10% 左右不然成片节奏拖沓。视频合成是工程密度最高的环节。图片本身是静态的直接拼图播放会像 PPT需要加 Ken Burns 效果——缓慢缩放或平移让画面“活”起来这一步在 FFmpeg 里用 zoompan 滤镜实现。整条管线还要立一个原则时长由音频驱动。先合成配音拿实际音频时长再倒推每张图该展示多少秒最后才拼视频。顺序反了后面必然出现音画不同步。3. 源码安装与部署流程从空目录到服务跑通的最小路径拿到这类源码包后别急着读代码先把“能跑起来”的最小闭环跑通。源码内一般会有 requirements.txt、main.pyFastAPI 服务、config 或 .env.example 文件。部署流程总共四步装环境、确认资源文件、改配置、启动服务。下面按这个顺序来。3.1 环境依赖Python 版本、FFmpeg 与 CUDA 怎么配对先说结论Python 建议用 3.10 或 3.11。3.9 以下有些新库装不上3.12 以上部分深度学习依赖还没跟上。FFmpeg 必须装版本 4.4 以上视频合成和音频时长探测都靠它。GPU 不是必须的取决于图片引擎怎么配。如果图片走云端 API纯 CPU 机器也能跑完全链路如果要在本地跑 SD 文生图NVIDIA 显卡 CUDA 几乎绕不开显存 8G 起步12G 比较舒服。TTS 同理用 edge-tts 的时候完全不需要 GPU。conda create -n ai_drama python3.11 -y conda activate ai_drama pip install -r requirements.txt ffmpeg -version这套命令的逻辑很直白先用 conda 隔离一个干净的 Python 3.11 环境避免和系统 Python 打架再装源码依赖最后用 ffmpeg -version 确认 FFmpeg 在 PATH 里。requirements.txt 里一般会出现 openai用来接各种兼容 OpenAI 格式的模型服务、fastapi、uvicorn、edge-tts、requests、python-dotenv。注意如果图片引擎用的是本地 SD还需要单独启动一个 SD WebUI 服务并开启 API 模式requirements.txt 不负责帮你装那一坨。3.2 模型权重与资源文件动手前先确认这三样这类项目启动时需要三类资源。第一是 LLM 的接入方式云端 API 只要一个 KEY 和 Base URL本地 Ollama 则要确认模型已经 pull 下来。第二是 TTSedge-tts 不需要模型文件离线方案需要权重目录检查配置里路径是否存在。第三是文生图云端 API 不需要本地权重本地 SD 要确认 checkpoint 文件和 WebUI 服务端口可用。最容易漏的不是模型是字体。字幕烧录依赖一个中文字体文件比如 Noto Sans CJK 或思源宋体。很多源码默认配置写的是 /usr/share/fonts 下的路径Windows 上压根不存在字幕渲染成方块基本都是这里出的问题。资源项云端方案本地方案部署前确认LLMAPI Key Base URLOllama 模型curl 一次对话接口TTSedge-tts 联网CosyVoice 权重路径生成一段 3 秒音频文生图云端 APISD WebUI API请求一次 txt2img中文字体下载 TTF/TTC同左路径可读且文件名无中文提示别一上来就下载几十 G 的本地模型。先用云端 API 把全链路跑通确认代码逻辑没问题再考虑换本地权重。先跑通再优化是这套流程里最省时间的做法。3.3 修改配置与启动服务跑通最小可运行闭环源码一般带一个 .env.example复制成 .env 后改核心配置。主要就这几项LLM_BASE_URLhttps://your-llm-endpoint/v1 LLM_API_KEYsk-xxx LLM_MODELgpt-4o-mini TTS_ENGINEedge-tts TTS_VOICEzh-CN-XiaoxiaoNeural IMAGE_ENGINEcloud IMAGE_SIZE720x1280 FONT_PATH/data/fonts/NotoSansCJK-Regular.ttc OUTPUT_DIR./output启动命令uvicorn main:app --host 0.0.0.0 --port 8000在另一个终端跑curl http://127.0.0.1:8000/health返回 {status:ok} 就说明服务起来了。接着用 Web 界面或直接调接口提交一句话生成任务。这里有一个部署阶段最常见的失败点端口 8000 被占用报错 address already in use。处理方式是换端口或者查占用进程。源码项目一般会在 README 里写端口号但 README 不会替你处理端口冲突。注意提交第一次生成任务前把日志级别调到 DEBUG。整个链路有四到五个外部调用哪个环节慢了一眼就能看出来。服务起来后第一次提交任务时先盯日志。一个正常的最小闭环大概 1 到 2 分钟产出成片如果 5 分钟还没动静多半不是卡在模型调用就是某个外部服务没起来。我有个习惯保持了很久每次改配置后先跑一个最小样例而不是直接跑完整任务。最小样例就是标题里的那种一句话输入几十个字代价低、反馈快。4. 核心实现拆解一句话进分镜表出这一章是整份源码里最值得逐行读的部分。四个模块的代码通常拆成四个 py 文件或一个 services 目录下面按数据流向拆给出可直接修改的骨架。下面的代码按这类项目的常见实现整理可以和自己手里的源码逐段对照。4.1 剧本生成代码LLM API 的 JSON 模式调用import json import os from openai import OpenAI client OpenAI( base_urlos.getenv(LLM_BASE_URL), api_keyos.getenv(LLM_API_KEY), ) SCRIPT_SCHEMA { title: string, genre: string, synopsis: string, characters: [{name: string, personality: string, voice: string}], scenes: [{ location: string, time: string, mood: string, plot: string, dialogues: [{character: string, line: string}] }] } def generate_script(prompt: str) - dict: resp client.chat.completions.create( modelos.getenv(LLM_MODEL), response_format{type: json_object}, messages[ {role: system, content: ( 你是一个短剧编剧。根据用户的一句话生成一个60-90秒的短剧剧本 不超过3个场景每个场景2-4句台词。 f必须严格按这个JSON结构返回{json.dumps(SCRIPT_SCHEMA, ensure_asciiFalse)} )}, {role: user, content: prompt}, ], temperature0.8, ) raw resp.choices[0].message.content return json.loads(raw)这段代码用的是 OpenAI SDK 的 JSON 模式response_format 指定 json_object 后模型会尽量输出合法 JSON。提示词里直接把 schema 传给模型比描述“要一个剧本”可靠得多。temperature 放在 0.8让题材发挥有一点多样性又不至于把结构写乱。参数说明LLM_BASE_URL 指向兼容 OpenAI 协议的网关本地 Ollama 也能这么接LLM_MODEL 换成实际可用的模型名。response_format 不是所有模型都支持如果 API 报错把这个参数删掉靠 system prompt 里的“必须返回 JSON”约束同时在解析侧兜底。4.2 角色与场景提取代码结构化解析与清单生成def extract_shots(script: dict) - list[dict]: # 角色表把外观锚点补进去供文生图保持一致 characters {} for ch in script[characters]: characters[ch[name]] ch.get(appearance, ch[name]) shots [] for scene in script[scenes]: # 场景内出现的角色从对话者倒推避免漏人 on_screen list({d[character] for d in scene[dialogues]}) visual_prompt ( fcinematic still, {scene[location]}, {scene[time]}, {scene[mood]}, , .join(f{name} ({characters[name]}) for name in on_screen) ) # 每个场景按对话拆成镜头一镜一句或一镜两句 for i, dlg in enumerate(scene[dialogues]): shot_type close-up if i 0 else medium shot shots.append({ scene_id: len(shots) 1, shot_type: shot_type, location: scene[location], visual_prompt: visual_prompt f, {shot_type}, character: dlg[character], line: dlg[line], # 中文按字数粗估时长后续以音频为准 duration: max(3.0, len(dlg[line]) * 0.18), }) return shots这段做了两件事。第一把角色名映射成带外观描述的角色串后续每一张图的 prompt 都带这段描述尽量维持人物一致。第二把 scene 拍平成 shot 列表每个 shot 对应一条台词和一段可渲染的提示词。duration 先用“字数乘 0.18 秒”粗估真实时长后面以音频为准这个字段只是给合成阶段一个初始值。容易踩的误区不要在 extract_shots 里做自然语言解析。这个函数的输入是已经结构化的 dict不是文本。如果剧本 JSON 缺字段宁可抛异常让上层重试也不要写一堆 if 猜来猜去。4.3 分镜扩展与提示词组装一场戏拆成 N 个镜头实际项目里 shots 不会只有对话一个维度还会按情绪插入空镜。通用规则是每个场景首镜用 wide shot 建立空间情绪转折处加一个 close-up结尾加一个无台词的空镜用于转场。def expand_shots(scenes: list[dict]) - list[dict]: shots [] for scene in scenes: shots.append({ type: wide, prompt: fwide shot, {scene[location]}, {scene[time]}, {scene[mood]}, line: , duration: 2.0, }) # 场景内对话镜头 for dlg in scene[dialogues]: shots.append({ type: medium, prompt: fmedium shot, {scene[location]}, {dlg[character]} speaking, line: dlg[line], duration: max(3.0, len(dlg[line]) * 0.18), }) # 场景结尾空镜用于转场 shots.append({type: empty, prompt: , line: , duration: 1.0}) return shots这个扩展逻辑放在提取之后、素材生成之前作用是给剪辑留出转场呼吸感。wide 镜头建立空间empty 镜头用于场景切换。empty 镜头不需要图片合成时用上一帧淡出来替代。参数说明duration 写死 2.0 和 1.0 对应快节奏短剧想做成慢情绪片改成 3.0 和 1.5。这套参数没有标准答案最终以配音后的实际时长为基准。4.4 图片与配音生成素材准备的并发与缓存素材准备是整个链路最耗时的一段通常按 shot 列表并行处理。下面是图片生成和 TTS 配音的骨架。import asyncio import aiohttp import edge_tts async def generate_assets(shots: list[dict]) - None: async with aiohttp.ClientSession() as session: for idx, shot in enumerate(shots): # 生成图片云端 SD API 示例 if shot[prompt]: await call_txt2img(session, shot[prompt], fassets/shot_{idx:03d}.png) # 生成配音无台词镜头跳过 if shot[line]: tts edge_tts.Communicate(shot[line], zh-CN-XiaoxiaoNeural, rate10%) await tts.save(fassets/shot_{idx:03d}.mp3) print(f[assets] shot {idx} done)这段体现了一个原则图片和配音按 shot 一一对应文件名用三位序号对齐合成阶段只需要按编号遍历。edge-tts 的 Communicate 直接传 rate 参数10% 就是短剧常用语速。参数说明call_txt2img 在不同项目里实现差异很大云端 API 和本地 SD WebUI 的请求体不同但共同点是返回一张图并写入指定路径。对齐时注意没有台词的空镜也要生成背景图不要给 FFmpeg 一个不存在的文件。4.5 视频合成代码FFmpeg 拼接、字幕与参数解释素材齐了之后合成阶段我不建议写 Python 逐帧处理直接调 FFmpeg。一个典型的分镜合成命令是ffmpeg -y \ -i assets/shot_001.png \ -i assets/shot_001.mp3 \ -filter_complex \ [0:v]scale720:1280:force_original_aspect_ratiodecrease,pad720:1280:(ow-iw)/2:(oh-ih)/2,zoompanzmin(zoom0.0015,1.2):d75:fps25,setsar1,formatyuv420p[v] \ -map [v] -map 1:a -c:v libx264 -c:a aac -t 3.0 -shortest out.mp4这条命令把一张静态图和一段音频合成一个带缓慢缩放效果的短视频。scale 加 pad 是把任意比例图片居中适配到 720x1280 竖屏防止拉伸变形。zoompan 里的 z 表达式做缓慢放大d75 配合 fps25 正好 3 秒画面不会死板。参数说明真正成片不是一条命令完成的而是逐段生成后 concat。每段视频时长以音频实际时长为准用 ffprobe 拿音频时长替换 -t 的值最后用 concat demuxer 拼成整片。字幕用 subtitles 滤镜烧录必须指定 fontfile写法是 subtitlessubs.srt:force_styleFontNameNoto Sans CJK SC,FontSize12。字幕文件要转成 UTF-8路径里不带中文字符这两点比较容易踩。5. 避坑与排查这套 AI 短剧生成链路最常见的五个翻车点代码跑通只是开始。真正让这套平台稳定产出的是知道它会在哪儿翻车。下面五条是我在这类项目里反复遇到的坑按出现频率排序。5.1 高频翻车点现象、原因、处理坑一字幕渲染出来全是方块。现象是字幕位置正常但文字全是□□□。原因基本是 subtitles 滤镜找不到中文字体或者 FontName 写的是展示名而不是 postscript 名。解决方法是先把字体文件放到一个纯英文路径然后用 fc-list :langzh 查字体实际注册名再写进 force_style。经验上思源黑体在多数 Linux 环境注册名就是 Noto Sans CJK SC。坑二成片音画不同步或者末尾黑屏几秒。现象是画面和台词对不上台词说完了画面还在动。原因是先按预估时长生成图片视频再对音频两边时长不一致。解决方法是改变顺序先合成配音用 ffprobe 量真实时长再倒推每张图的展示时长。这个原则能在这条链路上少走很多弯路。坑三图片被拉伸变形人物脸变宽。现象是竖屏视频里所有画面比例都不对。原因是 SD 生成尺寸没有跟随视频分辨率。处理办法是在合成命令里永远保留 scale pad 这段 filter并且 IMAGE_SIZE 固定成和成片分辨率一致。文生图 API 参数里的 size 也要同步改。坑四LLM 偶尔返回非法 JSON整个任务中断。现象是日志里 json.loads 抛 JSONDecodeError。原因是模型在长输出时偶尔会带注释或截断。处理办法是解析前做一次清洗取第一个 { 到最后一个 } 之间的内容再去掉尾部多余逗号仍然失败就重试一次重试仍失败就把任务标记为 failed不要无限循环。坑五TTS 长文本丢句子声音跳到下一句。现象是某段配音中间少了一句台词。原因是 edge-tts 对单次合成文本长度有限制超长会被截断。处理办法是按句号切分台词逐句合成再用 FFmpeg 拼接句间加 100ms 静音。每句独立失败重试的成本也低。5.2 排查速查表一条命令定位问题症状优先检查命令服务起不来端口占用 / 依赖缺失uvicorn 启动日志 / pip list图片全黑SD 服务未启动或 prompt 为空curl http://127.0.0.1:7860/sdapi/v1/txt2img配音为空文本超长 / 网络不通edge-tts --text 测试 --write-media test.mp3成片无音轨合成命令漏 map 1:affprobe out.mp4字幕方块字体路径不正确fc-list :langzh这张表的用法是“先缩小范围再改代码”。合成阶段出问题先单独跑 ffmpeg 命令不经过平台确认命令本身没问题再排查代码配音出问题先单独跑 edge-tts绕过整个平台。这样做能避免在错误的层级浪费一下午。6. 进阶批量生成流水线与成片质检不管改了什么配置我每天开工第一件事是跑一个 20 字以内的输入比如“男孩在教室捡到一张写满号码的纸条”。全链路冒烟的意义在于如果最小输入都失败就别浪费算力跑长任务。冒烟脚本要检查四个产物剧本 JSON、图片数量、音频数量、最终 MP4四个都非空才算通过。批量生成时不要开几十个线程同时打 LLM 和 SD常见做法是队列加一组 worker。任务进来先写任务表worker 从队列取一个处理一个失败重试 3 次第 3 次仍然失败就写入 fail.log 并继续下一个。临时文件命名统一加 uuid 后缀避免两个任务互相覆盖。这套机制搭好后单机跑几十条短剧完全够用。每批成片我习惯抽 10% 用 ffprobe 验三样时长和音频一致、分辨率为 720x1280、有音轨。人工抽检看字幕错行和配音情感这两样机器很难自动判。这条流水线跑到最后最值钱的反而不是单个模型而是可重复的排产流程——今天能出一条明天也能出一条。个人习惯是每天第一条冒烟没过就不排产这个习惯帮我挡掉了好几次批量全废的损失。希望帮到你。本文还有配套的精品资源点击获取
返回列表