简介:AI短剧生成平台源码包(附安装部署流程)面向短视频创作者、独立开发者和AI应用爱好者,解决短剧制作中剧本、分镜、配音、合成等环节碎片化、流程冗长的问题。只需一句话输入,即可借助大语言模型完成剧本改写、角色与场景提取、分镜拆解,并通过AI绘图生成角色形象和场景背景,最终以文生视频/图生视频、TTS配音及FFmpeg合成自动产出成片。包体共94个文件,约634KB,主要包含56个TypeScript服务端与脚本文件、8个Vue前端页面、6个JSON配置、Docker部署编排及README说明,功能涵盖项目与剧集管理、角色与场景编辑、音频生成及视频导出,目录结构清晰,便于二次开发与本地部署。已有327人学习下载。读者可获得完整前后端源码、部署所需的docker-compose与Dockerfile、AI角色批量生成与分镜管理逻辑,以及从项目创建到整集导出的全流程实现参考。
1. AI短剧生成平台:一句话输入到成片输出的全流程拆解
短剧生产最贵的是人力:编剧写本、导演分镜、配音配乐、剪辑合成,四五个角色凑齐了才能出片,一条 60 秒的片子往往要磨一整天。这份资源把整条链路压缩成一个单入口的自动化平台——你输入一句剧情梗概,它就依次完成剧本扩展、角色和场景提取、分镜生成、配音合成、视频合并,最后交出一段可播放的 MP4。它不是单点功能的 demo,而是自带源码和安装部署流程的完整系统。适合做短视频矩阵的运营、想批量验证剧情创意的团队,以及想在 AI 视频生成方向做二次开发的工程师。
2. 平台架构与数据链路:剧本、分镜、配音、合成四段怎么串
2.1 输入提示词是怎么拆成剧本、角色、场景的
我拆这份源码时,第一印象是它没有把"AI 生成"做成一个大黑匣子。入口main.py接收--input参数后,先走modules/script_gen.py,把一句话丢给大模型,要求它返回固定结构的 JSON 剧本。这个 JSON 是后面所有环节的数据源:角色列表、场景标签、每一幕的台词都写在里面。
这里最关键的一点是提示词模板。模板里写死了"只输出 JSON,不要解释文字"。我听很多做过类似工具的人说,不写死这一步,模型就会在 JSON 前后夹带自然语言说明,json.loads()直接崩。所以我在自己的项目里学了这个习惯:给大模型的 system 提示词里永远附一个 JSON Schema 示例,并要求"只输出 JSON"。这算是少踩一半解析坑的诀窍。
# modules/script_gen.py import json DRAMA_TEMPLATE = """ 你是短剧编剧。用户会给出一个剧情梗概,请把它扩展成 4 幕短剧剧本。 只输出 JSON,不要输出任何解释文字。JSON 结构必须为: { "title": "剧名", "characters": [{"name": "角色名", "role": "主角"}], "location": ["主要场景"], "acts": [ {"title": "幕名", "summary": "这一幕概要", "dialogue": ["台词1", "台词2"], "scene_tags": ["场景标签"]} ] } """ def generate_script(user_input: str, llm_client) -> dict: resp = llm_client.chat( system=DRAMA_TEMPLATE, user=user_input, temperature=0.7, max_tokens=1500 ) script = json.loads(resp) if len(script["acts"]) < 4: raise ValueError("剧本幕数不足 4 幕,重新生成或提高 max_tokens") return script提示:temperature=0.7 是本平台的默认值。想要稳定复现同一个剧本改到 0.5 以下,想探索不同情节分支才调到 0.9 以上。max_tokens 低于 900 时,长剧情经常被截断,导致最后一幕缺失。
这段代码的实际作用是:把用户的--input作为user消息,把模板作为system消息,一次性拿回结构化剧本。temperature=0.7是我常用的折中值——剧情要有点随机性,但台词不能离谱到没法用。max_tokens=1500保证 4 幕剧带台词回复完整,不会因为输出长度限制被腰斩。
拿到剧本之后,modules/scene_extract.py做角色和场景的抽取。它不直接生成视频,而是先把"有哪些角色、有哪些场景、每一幕需要的画面提示词"列成清单。角色抽取靠读取characters字段,场景抽取靠遍历每一幕的scene_tags,随后把二者合并成文生图提示词。这一步决定了后面分镜的素材质量。
# modules/scene_extract.py def build_image_prompts(script: dict, style: str) -> list: prompts = [] for idx, act in enumerate(script["acts"]): tags = ",".join(act["scene_tags"]) chars = ",".join([c["name"] for c in script["characters"]]) prompts.append( f"{style}, 场景:{tags},人物:{chars},镜头:中景,短剧画面" ) return prompts参数说明:style来自配置里的video.style,默认为"电影感,冷色调"。你可以把风格改写成"明亮暖色、甜宠剧"或"昏暗、惊悚",这直接影响分镜生成画面的质感。中景是默认镜头,在后续分镜干预时才会细化到特写、俯拍、跟拍。如果你想给某个固定场景预设镜头,可以在配置里加一个scene_tags.camera_map,比如把"便利店"映射成"手持近景"、"楼道"映射成"低角度广角",这样生成的提示词会自动带出对应镜头。
2.2 分镜、配音、合成之间的数据契约
分镜、配音、合成三段不是各干各的最后硬拼,而是通过一个统一的manifest.json串联。每一段生成的内容都带shot_id,合成器按shot_id顺序拼装。下面是分镜清单里一个镜头的结构:
{ "shot_id": "shot_003", "scene": "凌晨的便利店", "characters": ["店员", "神秘顾客"], "camera": "中景,低角度", "dialogue": "你每天凌晨三点都来买同一瓶牛奶", "image_file": "storyboard/shot_003.png", "audio_file": "audio/shot_003.wav", "duration": 4.5 }看到这个结构就清楚整个时间线是怎么对齐的:dialogue用来生成配音,audio_file是配音模块的输出,duration是这一镜头的时长,合成器把每个镜头的画面和音频按shot_id顺序写入输出队列。如果配音生成失败,audio_file缺失,合成器会标记这一镜并跳过,不会把整个视频搞崩,这就是中间产物落盘的意义。
使用这套契约还有一层好处:你可以在不重跑模型的情况下,单独替换某一个镜头的画面或音频文件,只要保持文件名和原有shot_id对应,合成器就会拾取新文件。我两次遇到配音口型对不上的情况,都是直接把 wav 换掉,而不是重新整段合成。
2.3 断点续跑:为什么中间产物全落盘
我拆到pipelines/short_drama.py时,发现一个值得抄的设计:五个阶段(script、scenes、storyboard、audio、video)全部落盘,重跑时若发现某个阶段的 manifest.json 已存在就直接跳过。这个设计在实际跑片时特别省时间,尤其是 storyboard 阶段,每张图都是一次模型推理,要是配音阶段挂了就得从头再抽图,能把人搞到怀疑人生。
# pipelines/short_drama.py from pathlib import Path import json STAGES = ["script", "scenes", "storyboard", "audio", "video"] def _load_stage(stage: str, out_dir: str): mf = Path(out_dir) / stage / "manifest.json" return json.loads(mf.read_text(encoding="utf-8")) if mf.exists() else None def run_pipeline(user_input: str, cfg: dict): out_dir = Path(cfg["video"]["output_dir"]) done = {} for stage in STAGES: cached = _load_stage(stage, str(out_dir)) done[stage] = cached if cached else run_stage(stage, done, cfg) return str(out_dir / "video" / "final.mp4")这段是平台最核心的编排代码。STAGES定义了流水线顺序;_load_stage检查某个阶段是否已产出;done把前一阶段的产物传给下一阶段。我一般会保留这个落盘机制,它带来的好处不是多一点磁盘空间的事,而是让整个系统可以随时中断、随时续跑。实际跑片时,我经常在 storyboard 生成一半时看到角色形象不对,直接改掉提示词后从 storyboard 阶段恢复,之前抽好的图不受影响。
3. 安装部署流程:环境、配置与首次跑通一条样例
3.1 环境要求与依赖安装
这份源码的依赖集中在requirements.txt,核心是 PyTorch、diffusers、openai 兼容客户端、edge-tts、ffmpeg-python。部署前我建议先确认三件事:Python 版本、CUDA 驱动、FFmpeg 可执行文件。Python 用 3.10 或 3.11 都行,3.12 上个别依赖会出现编译问题,没必要冒险。
nvidia-smi python --version ffmpeg -version三条命令分别确认 GPU 驱动、解释器版本和 FFmpeg。如果nvidia-smi显示 CUDA 12.x,后续装 PyTorch 就选 cu121 的安装源;如果机器没有 NVIDIA GPU,那只能跑 CPU 模式,速度会慢很多,分镜生成阶段从分钟级变小时级,基本只适合验证流程,不适合真正出成片。
接下来创建虚拟环境并安装依赖。我习惯把所有 Python 依赖都锁在 venv 里,不用系统环境,否则后面装其他项目时 torch 版本互相打架,翻车概率很高。
git clone <你的仓库地址> ai-short-drama cd ai-short-drama python -m venv venv source venv/bin/activate python -m pip install --upgrade pip pip install -r requirements.txt逻辑说明:python -m venv venv创建隔离环境,source venv/bin/activate激活(Windows 下用venv\Scripts\activate),pip install -r requirements.txt把依赖一口气装完。如果网络源不稳定,可以在 pip 后面加-i https://pypi.tuna.tsinghua.edu.cn/simple,这是国内常见的加速做法,能省掉很多等待时间。
装完先做一次冒烟测试,确认 torch 能调用 GPU:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"如果输出True,说明 GPU 路径通了。输出False但也不报错,说明装的是 CPU 版,需要重装 GPU 版。这里有个坑:requirements.txt 里如果只写torch不带索引,pip 往往默认装 CPU 版,因为官方源里 GPU 版体积太大,很多镜像站只有 CPU 版。解决办法是明确指定 cu118 或 cu121 安装源。这块我会在第 5 章展开讲。
3.2 配置文件:模型服务、配音引擎与视频参数
平台提供一个config.example.yaml模板,复制成config.yaml后按自己环境改。核心配置是三个块:llm(剧本/分镜文本生成)、tts(配音)、video(画幅与帧率)。
| 配置项 | 示例值 | 说明 |
|---|---|---|
| llm.provider | openai_compatible | 本地模型服务或云端兼容接口都行 |
| llm.base_url | http://127.0.0.1:8000/v1 | 本地大模型服务的地址 |
| llm.model | qwen2.5-14b-instruct | 剧本生成模型 |
| tts.engine | edge-tts | 当前免费且稳定的配音引擎 |
| tts.voice | zh-CN-XiaoxiaoNeural | 中文女声 |
| video.width / height | 720 / 1280 | 竖屏短剧画幅 |
| video.fps | 30 | 输出帧率 |
llm: provider: "openai_compatible" base_url: "http://127.0.0.1:8000/v1" model: "qwen2.5-14b-instruct" api_key: "sk-local" tts: engine: "edge-tts" voice: "zh-CN-XiaoxiaoNeural" rate: "+8%" video: width: 720 height: 1280 fps: 30 output_dir: "./outputs" style: "电影感,冷色调" batch_size: 1参数怎么改:如果你用的是云端兼容接口,base_url换成服务商地址,api_key换成真实密钥;tts.voice想换男声就改成zh-CN-YunxiNeural;rate是语速,正数加快,负数放慢,改成-4%适合悬疑剧的压抑感。batch_size务必先设成 1,显存不够时再批量会直接 OOM。api_key填sk-local是因为本地服务通常不做密钥校验,但字段不能留空,否则客户端请求会报鉴权错误。
3.3 首次启动:一条命令行把链路跑通
配置写好后,跑一个最小的样例。这句话是实践中最希望有人提前告诉我的:不要上来就跑长剧,先用一个 30 到 60 秒的短剧把链路打通,确认五个阶段全部 done,再谈参数调优。
python main.py --input "外卖骑手在暴雨夜接到最后一单" --duration 60命令含义:--input是那一句话剧情梗概,--duration是目标时长。脚本执行时日志会依次打出[stage script] done、[stage scenes] done、[stage storyboard] done、[stage audio] done、[stage video] done。等看到outputs/外卖骑手/video/final.mp4,就说明部署成功。
输出目录结构大概是这样的:
outputs/ 外卖骑手/ script/manifest.json scenes/manifest.json storyboard/shot_001.png storyboard/shot_002.png audio/shot_001.wav video/final.mp4检查这个目录是部署后的标准动作。我要确认 manifest 里没有error字段,storyboard 里图片数量大于等于 4,audio 目录下每个 wav 都能播放。做到这步,平台就能真的用了,后面才是调提示词、调画质、调配音语速的活。
4. 实战参数与产物调整:提示词、分镜干预与导出细节
4.1 提示词设计:人物、地点、冲突三要素
平台对输入那句话的要求不高,但也不是随便写一行就有好效果。我拆项目时看到的结果是:包含"人物、地点、冲突"三个要素的提示词,生成的剧本完成度远高于只写"一个人遇到了奇怪的事"这种空洞描述。
| 示例 | 结果倾向 |
|---|---|
| 外卖骑手在暴雨夜接到最后一单 | 有冲突点,结局能落地 |
| 女主播下播后收到十年前自己发来的私信 | 能自动生成悬疑反转 |
| 一个人发现了秘密 | 剧情发散,角色和场景抽取不稳定 |
一个合格输入的写法是:[人物] + [具体地点] + [一件打破常规的事]。打破常规越具体,剧本越不容易被大模型生成成流水账。比如"暴雨夜"和"最后一单"这两个限定词,比"都市"和"一个订单"更能触发强冲突。我第一次试的时候输入"两个人吵架",产出的剧本平平无奇;改成"夫妻在小区门口因为一袋垃圾吵架,路过的保安认出女方是小时候的邻居",剧本直接出现了反转结构。
python main.py \ --input "女主播下播后收到十年前自己发来的私信" \ --style "悬疑+轻惊悚" \ --duration 90 \ --scene-density high--style覆盖配置文件里的全局风格,--scene-density控制分镜密度:high表示同一个场景拆成更多镜头,适合情绪戏;low减少镜头数,适合对话密集的短场景。第一次跑建议先用默认值,跑完看分镜数量再决定要不要调。分镜数量太多会直接拉高生成时长,一条 90 秒的片子,high 密度下 storyboard 阶段可能要跑十几分钟,而 low 密度可能三五分钟就完事。
4.2 不重新生成也能改的地方:直接改分镜清单
平台的优势是中间产物全落盘,所以你想改某一镜的台词、镜头角度、时长,不用重新生成整个剧本。直接改storyboard/manifest.json里对应镜头的字段即可。这里有个小坑:改duration后必须删掉audio阶段的 manifest,让配音重新生成,否则新时长和老配音会错位。我只改台词不改时长的时候,也习惯把 audio manifest 一起删掉重跑,因为文本一变,配音的重音和停顿就变了,硬留着旧配音会显得声音情绪和台词对不上。
手动改完分镜,重新运行并指定从 storyboard 之后继续:
python main.py --input "女主播下播后收到十年前自己发来的私信" --resume-from storyboard--resume-from storyboard会让平台跳过剧本和场景抽取,直接从分镜开始。注意:这里--input必须和最初那次一致,因为平台按输入文本生成输出目录名,文本变了会新建目录,导致找不到原来的中间产物。这个机制很笨但很可靠,我改脚本时保留了同样的逻辑。
4.3 视频合成与导出:合成器到底做了什么
到了最后一步,合成器把分镜图和配音按时间线拼起来。它内部其实封装了 FFmpeg,只是把拼接、加字幕、转码这些繁琐命令统一成了对 manifest 的读取。看日志时,你会看到类似下面的命令被打印出来:
ffmpeg -f concat -safe 0 -i concat_list.txt -c:v libx264 -pix_fmt yuv420p video/final.mp4参数说明:-f concat -safe 0 -i concat_list.txt是按清单拼接,-safe 0允许非 ASCII 路径;-c:v libx264指定 H.264 编码,-pix_fmt yuv420p是兼容性最好的像素格式,手机上也能直接播。如果合成后无法在微信里播放,先检查是不是漏了yuv420p。如果你拿到的中间产物不是单张 PNG,而是多个 ts 视频片段,合成器同样可以用 concat 把它们合成一个完整成片。ts 片段在拼接时不需要重新编码,命令会用-c copy,速度更快,但只适用于同编码、同分辨率的片段。
合成器还有一个字幕开关:配置里的video.burn_subtitles设为 true,它会在合成时把dialogue烧录进画面。烧字幕走的是硬编码,而不是外挂字幕轨,好处是所有播放器都能看到字,坏处是后期想改字幕就得重新合成一遍。我实际跑片时习惯先不开字幕,等确认台词没问题后再开,否则改一次字幕就等于重新合一次视频。
5. 避坑指南:部署和跑片阶段最常见的六个坑
5.1 torch 依赖冲突:GPU 版和 CPU 版混着装
现象:按requirements.txt装完后运行torch.cuda.is_available()返回 False,或者直接报CUDA driver initialization failed。
原因:requirements.txt里写的torch没有绑定 CUDA 版本,pip 从默认源装了 CPU 版;还有一种情况是机器上装了多套 CUDA,PyTorch 找到的库和驱动版本对不上。
解决:先敲nvidia-smi看驱动支持的 CUDA 版本,然后按对应版本重装。我常用的组合是 CUDA 11.8 配torch==2.1.2+cu118,CUDA 12.1 配torch==2.2.2+cu121。装完再跑一次torch.cuda.is_available(),直到输出 True 再继续。
5.2 显存不足:分镜批量生成直接 OOM
现象:单条提示词跑没问题,--scene-density high后进程在生成第三四张分镜时被杀,日志末尾是CUDA out of memory。
原因:video.batch_size被默认调到了 2 以上,多个画面同时推理,显存峰值成倍增长。我的显卡是 8G 显存,一次只能扛住 512x512 的单张生成。
解决:在config.yaml里把batch_size改成 1,width/height从 720x1280 降到 512x512 试跑。确认稳定后再逐步往上加。另外,Linux 下可以设CUDA_VISIBLE_DEVICES=0让进程只认一张卡,避免多卡调度带来的额外开销。跑长剧时把--scene-density保持默认,等整体稳定再开 high,否则很容易跑到一半被系统杀进程。
5.3 配音和画面不同步:时长对不上导致一刀切
现象:生成的视频里,台词说到一半画面已经切到下一个镜头,或者画面停在那里等台词说完。
原因:分镜manifest里的duration是预估时长,配音引擎真实生成的 wav 比它长或短时,合成器按duration硬切,声音被切掉或留白。
解决:先删掉audio/manifest.json,再跑一次让配音重新按分镜生成;同时在config.yaml里确认tts.rate没有设置得过慢。经验值是语速+0%到+8%时,15 字以内的台词和 4~5 秒镜头基本同步。实在要对齐,就把duration改成max(duration, audio_duration) + 0.3,给留白一点余量。我早期跑悬疑剧时把 rate 调成-20%,结果每条配音都比画面长两秒,后来统一改成在合成器里做时长自适应,才彻底摆脱这个问题。
5.4 角色形象前后不一致:同一人物每张图长得都不一样
现象:第一个镜头里主角穿红色外套,第二个镜头里变成蓝色,人脸也明显不是同一个人。
原因:文生图模型只看单帧提示词,没有记忆。平台里如果没启用角色参考图,那么同一角色在每个分镜里都会重新生成一张脸。
解决:尽量在提示词里固定角色的服装和发型描述,比如"红色外套、短发、圆脸"。更彻底的做法是配一张角色参考图,在config.yaml里指定character_refs,让分镜生成时以参考图作为条件,这个我在第 6 章展开。特别是短剧里主角出场镜头多,没有参考图的方案跑出来就是换脸剧,根本没法治。
5.5 合成黑屏:RGB 与 BGR 颜色通道翻转
现象:final.mp4能打开、有声音,但画面是全黑或者颜色完全失真。
原因:Python 侧用 PIL 读图得到的通道顺序是 RGB,FFmpeg 解码时按 BGR 处理,接缝处颜色被翻转,某些解码器直接出黑屏。常见触发点是转场帧用了透明通道的 PNG。
解决:在合成脚本里读图后统一做一次cv2.cvtColor(img, cv2.COLOR_RGB2BGR),输出前再用-pix_fmt yuv420p转码。如果项目里用的是imageio,读图后也多做一步通道转换。这个问题最气人的地方是它不报错,日志全绿,打开视频才发现坏了,所以我现在每次合成完都会先抽一帧看颜色再整片播放。
5.6 中文路径和字符编码问题
现象:Windows 下输出目录带中文,FFmpeg 报找不到文件,或者生成的 MP4 文件名乱码。
原因:平台默认用 UTF-8 写路径,而 Windows 控制台编码是 GBK,传到 FFmpeg 后路径匹配不上。
解决:把output_dir改成纯英文,比如outputs/drama_run_001;在bash里启动前设export PYTHONUTF8=1。输出文件名里的中文只保留在manifest.json内部,不要硬塞进文件名。我在 Windows 上跑过两次,全栽在这个坑里,后来不管在哪台机器上部署,第一件事就是把输出目录改成纯英文路径。
6. 进阶用法:角色形象一致性与批量短剧流水线
6.1 让角色形象跨镜头一致:参考图与负向提示词
短剧最怕的是主角每换一幕就换一张脸。涉及 AI 生成角色形象时,参考图是最实用的解法。平台支持在config.yaml里指定角色参考图,让分镜生成时参考固定形象。做法是把角色的一张半身照放进refs/目录,再在配置里登记角色名和图片路径。
character_refs: - name: "女主播" image: "./refs/wang_mian.png" traits: "波浪长发,红色耳环,浅蓝衬衫" negative: "模糊脸, 五官变形, 多余手指, 低画质"traits是给文生图模型的固定形象描述,每次生成该角色时自动拼进提示词;negative是负向提示词,专门排除畸变和画质问题。用上参考图后,同一角色在不同分镜的平均相似度明显提升,至少不会一个镜头一个样。
6.2 批量短剧流水线:从手动一条到一跑一批
当你通过了第 4 章的实操,下一步自然是想批量出短剧。平台源码里没有内置批量模式,但原结构很容易扩展。我通常写一个简单的tools/batch_pipeline.py,读一批提示词循环调用main.py。这套改法一旦跑通,平台就从单条短剧的工具变成一条 AI 短视频生成平台级别的批量链路。
# tools/batch_pipeline.py import json import subprocess cases = [ {"prompt": "外卖骑手在暴雨夜接到最后一单", "style": "都市"}, {"prompt": "女主播下播后收到十年前自己发来的私信", "style": "悬疑"}, {"prompt": "修表匠发现顾客送来的手表倒着走", "style": "奇幻"}, ] for case in cases: print("running:", case["prompt"]) subprocess.run( ["python", "main.py", "--input", case["prompt"], "--style", case.get("style", "都市")], check=True, )参数说明:check=True保证任何一条失败都会中断整批,避免后面全跑空;cases可以换成从 CSV 读入,批量维护文案。跑批时建议--scene-density用默认,等整体稳定后再开高密度,否则 OOM 会打断整批任务。批量跑的另一个好处是,你可以在文案里统一加"人物、地点、冲突"结构,让每一批的产出质量都在及格线以上。
从那以后我每次把平台架到新机器上,都强制先走一遍三件事:看nvidia-smi确认驱动版本,检查五个阶段的 manifest 是否齐全,最后才允许自己点下合成按钮。这套顺序帮我排掉了八成的环境类报错。希望帮到你。
本文还有配套的精品资源,点击获取