简介:这是一套面向AI漫剧与短剧创作者、视频内容开发者的本地化全流程创作工具,核心解决从故事到成片的高效产出难题。资源整合剧本分析、AI分镜、图片资产与Seedance火山方舟视频生成,支持AI真人剧与AI漫剧在本地完成制作,数据不出本机即可完成短剧工作流管理。压缩包共1175个文件,以1125个webp图片资产为主,辅以js/json配置、Docker/nginx等部署配置,以及png/jpg/svg图形资源,整体仅19.84MB,轻量便于部署。已有50人学习,适合想深度定制AI视频生成流程、搭建自有短剧创作平台的中高级创作者。从这套工程中可获得可直接运行的前后端框架、容器编排与反向代理配置、浏览器端交互页面,能够快速复现本地化AI漫剧生产流水线,显著降低从剧本分析到视频合成的试错成本。
1. AI 短剧全流程创作工具:把从剧本到 Seedance 成片的脏活一次串完
做 AI 短剧的同行应该都有同感:真正卡住进度的不是最后那一下视频生成,而是生成前没人替你收拾的结构化工作。剧本停在口头创意、分镜靠手感、角色图和场景图各存各的,真到 Seedance 上生成时,才发现提示词、首帧、时长根本没对齐。这套 AI 漫剧 / AI 短剧全流程创作工具,就是把剧本分析、AI 分镜、图片资产、Seedance×火山方舟视频生成的链路完整串起来的一套工程包。适合自己攒短剧的个人创作者,也适合做漫剧分镜设计的动画工作者;跟着章节走一遍,你能拿到一份可以直接替换成自己内容的分镜脚本 JSON、一份角色一致性的素材库方案,以及一套跑通视频生成的调用骨架。下文所有操作都按我实际跑过的顺序来写,参数给到可直接抄的程度。
2. 剧本分析:把文案拆成可执行的分镜脚本,拆错等于全盘重来
2.1 为什么要先做「结构化拆解」,而不是直接去生成分镜
短剧剧本本质是叙事文本,里面是故事线、对白和情绪,模型读不懂「运镜」和「景别」。如果你把一段三百字的故事直接丢给图生视频模型,它只会按自己的理解自由发挥,出来的画面大概率跟你的预期两不相干。我见过太多人在这里翻车:剧本写得热血沸腾,生成出来的视频像新闻播报,原因是模型没拿到任何镜头信息,全靠猜。
所以必须先有一个中间格式:分镜脚本。它把每一段文字对应到「什么场景、什么角色、什么动作、什么景别、镜头怎么动、大概几秒」。这一步做完,后面 AI 分镜和视频生成才有依据。常见做法是让大模型(比如豆包、GPT,或你在 Coze 里接的 LLM)先把剧本清洗成结构化文本,再交给脚本做场景切分;如果你剧本本身格式规整,直接跑解析脚本也行。这套工具包里的剧本分析模块走的就是「LLM 清洗 + 脚本切分」的组合路子,清洗这一步可以手动做,重点是输出格式要统一。
2.2 一份可直接抄的分镜脚本 JSON 结构
分镜脚本的格式决定了后面所有环节能不能自动衔接。我用的结构是把「场景 scene」作为第一层,每个场景下面挂「分镜 shot」。一个 shot 将来就对应一张分镜底图、一条 Seedance 生成任务,这个对应关系是整条工作流的锚点。
{ "story_title": "老街的猫", "scenes": [ { "scene_no": 1, "location": "老街巷口", "time": "黄昏", "shots": [ { "shot_no": "S1-01", "shot_type": "全景", "camera_move": "缓推", "content": "猫蹲在屋檐上,看着远处亮灯的小卖部", "dialog": "", "sound": "巷子里的风声", "duration": 3 }, { "shot_no": "S1-02", "shot_type": "中景", "camera_move": "固定", "content": "猫站起身,尾巴轻轻扫过瓦片", "dialog": "阿桔:今晚该去讨点鱼干了。", "sound": "环境音", "duration": 4 } ] } ] }各字段的作用我在下表里说明,后面脚本和 Seedance 调用都会引用这些字段名,所以命名务必统一。
| 字段 | 类型 | 说明 |
|---|---|---|
| story_title | string | 故事名,用于命名输出文件夹 |
| scene_no | int | 场景序号,决定文件分组 |
| location | string | 场景地点,必须和图片资产库里的场景名一致 |
| time | string | 时间段,影响灯光描述词 |
| shot_no | string | 镜头编号,全局唯一,建议「S场景-序号」格式 |
| shot_type | string | 景别:远景/全景/中景/近景/特写 |
| camera_move | string | 运镜:固定/缓推/拉远/横移/跟随 |
| content | string | 画面内容,中文描述动作与主体 |
| dialog | string | 对白,留空表示无对白 |
| duration | int | 预估时长(秒),后续视频生成直接使用 |
这套结构里,一个 shot 对应一条生成任务,shot_no 会成为所有中间文件的命名前缀。你拿到工具包后不需要改结构,只替换内容字段即可。
2.3 一段能把剧本切成场景的解析脚本
我一般会把剧本按「空行分场景、非对白首行写环境、对白行写台词」的约定整理好,再跑下面这个脚本,产物就是标准的 storyboard.json。这个约定看着简单,但能让解析逻辑稳定下来,比让模型自由发挥可靠得多。
import re import json raw = open("script.txt", encoding="utf-8").read() # 按空行拆分,得到一个个场景块 blocks = [b.strip() for b in raw.split("\n\n") if b.strip()] # 匹配 "角色名:台词" 行,冒号中英文都兼容 line_re = re.compile(r"^(?P<role>[\u4e00-\u9fa5A-Za-z]+)\s*[::]\s*(?P<text>.+)$") scenes = [] for idx, block in enumerate(blocks, 1): scene = {"scene_no": idx, "location": "", "shots": []} for line in block.split("\n"): m = line_re.match(line.strip()) if m: # 每个对白行先当做一个潜在镜头,后续可再拆 scene["shots"].append({ "shot_no": f"S{idx}-{len(scene['shots']) + 1:02d}", "role": m.group("role"), "dialog": m.group("text").strip(), }) elif line.strip(): # 非对白的首行文本作为场景描述 scene["location"] = line.strip() scenes.append(scene) with open("storyboard.json", "w", encoding="utf-8") as fp: json.dump(scenes, fp, ensure_ascii=False, indent=2) print(f"解析完成,共 {len(scenes)} 个场景")逻辑说明:脚本先按空行把剧本拆成场景块,这是基于「一段一场景」的写作约定;接着逐行匹配「角色名:台词」格式,基于它生成 shot 雏形;非对白行则作为 location 字段。这样生成的 JSON 会和 2.2 节的结构基本一致,只是每个 shot 还缺少 shot_type、camera_move 这些信息,需要在下一步由人来补,或者用 LLM 批量补齐。
参数说明:line_re的正则里,\s*用来兼容中英文冒号前后的空格,[\u4e00-\u9fa5A-Za-z]+只匹配中英文角色名,避免把场景描述误判成对白。如果你的剧本里角色名带数字或符号,把字符集扩一下就行。脚本输出的storyboard.json就是后面分镜、资产、Seedance 调用共同读取的唯一数据源,所以这一步跑完先打开 JSON 目检一遍,确认 location 没被对白行污染再继续。
3. AI 分镜生成:从分镜脚本到分镜底图,提示词该怎么拼
3.1 分镜提示词的模板化拼接:角色库 / 场景库 / 运镜库
分镜底图的质量直接决定最终视频的上限,而提示词是最容易失控的环节。我的习惯是按「角色库 + 场景库 + 运镜库」三块词表拼提示词,每块词表固定写法,这样同一个角色在不同镜头里描述才能保持一致。角色库管谁在画面里,场景库管背景和光影,运镜库管镜头语言。
def build_prompt(character, action, location, shot_type, camera_move, style="赛璐璐动画,高饱和配色,黄昏暖光,电影感"): template = ( "{style},{shot_type},{camera_move}," "画面主体是{character},{action},背景是{location}" ) return template.format( style=style, shot_type=shot_type, camera_move=camera_move, character=character, action=action, location=location, ) # 示例调用 prompt = build_prompt( character="一只橘白相间的胖猫,耳朵有缺口,眼睛是琥珀色", action="蹲在屋檐上,尾巴轻轻扫过瓦片,看向远处小卖部", location="老街巷口的瓦房屋顶,远处有亮灯的小卖部招牌", shot_type="中景", camera_move="缓推", ) print(prompt)逻辑说明:style是全局风格锚点,同一部剧从头到尾固定这一句,不要每张图换风格描述,否则分镜之间像两部片子。character来自角色资产库里的固定描述词,action则来自 storyboard.json 里的 content 字段。模板的价值在于把变量隔离,角色和场景的描述词各自维护、独立迭代。
参数说明:style 里我强烈建议带上「赛璐璐、厚涂、写实、水墨」这种一级风格词,再补一个「黄昏暖光」这种光效词。光效词决定整组底图的色调统一度,这是漫剧和短剧最容易出彩也最容易翻车的点。action 字段只写一个主动作 + 一个次要动作,别把「站起来、伸懒腰、跳向招牌」三个动作挤在一起,后面视频生成会乱。
3.2 参数怎么设:步数、CFG、分辨率与长宽比
分镜底图我一般走 ComfyUI 批量出图,这样可以把 3.1 的提示词函数接到工作流里,按 storyboard.json 自动跑。漫剧场景下,画面构图比细节纹理重要,参数不必往极致里调,够用就行。下面是我验证过的一组起点参数,不同渲染器(比如 SD1.5 和 SDXL)取值会略有浮动,但大方向一致。
| 参数 | 建议值 | 说明 |
|---|---|---|
| 采样步数 steps | 25~30 | 漫剧不需要堆到 40 步,收益很小 |
| CFG | 4.5~7 | 越高越贴提示词,但容易脏;角色一致性场景建议偏低 |
| 采样器 | DPM++ 2M Karras | 出图稳定,动画风格友好 |
| 分辨率 | 1088x1920(竖版 9:16) | 短剧必须竖版,直接用方舟支持的档位 |
| 种子 seed | 固定 | 同一角色/场景复用同一个 seed,能显著减少变脸 |
| 批量大小 | 4~8 | ComfyUI 里一次跑多个 shot,注意显存上限 |
为什么 CFG 是关键参数:分镜阶段我们追求的是「同一角色不同镜头长得像」,CFG 拉到 7 以上时模型会过度响应提示词里的细碎描述,反而把角色特征画飞。我一般控制在 6 附近,配合固定 seed 使用。分辨率直接对齐火山方舟 Seedance 支持的档位,免得后面生成视频时还要二次裁剪。
3.3 保证分镜底图「一个角色一张脸」的做法
角色一致性是漫剧分镜里最玄学的一环。你生成十张图,同一个角色可能出现五张不同的脸,这不是模型笨,是提示词和参数没有约束住它。我常用的方案是「固定 seed + 低 CFG + 参考图引导」三者叠加。ComfyUI 里把第一张满意的角色正脸图作为参考图接入 ControlNet(用 IPAdapter 或 reference-only 模式),后续所有分镜在这个参考约束下生成,脸型、瞳色、毛色基本不会跑偏。
负面提示词也值得单独维护一份,我固定在用的一组是:模糊,低质量,多余的肢体,变形的脸,夸张的阴影,文字水印,logo。其中「文字水印」必须常驻,漫剧画面里一旦出现乱码文字,后面对齐首帧时会非常显眼。这套工具包里的 ComfyUI 工作流 json 已经把这些节点接好了,你只需要把 3.1 的 build_prompt 输出接到工作流的文本输入框,再替换角色参考图即可。批量出图完成后,按 shot_no 编号归档,下一步图片资产整理才能对得上。
4. 图片资产:把一次性的图变成可复用的素材库
4.1 资产目录与命名规范
分镜底图只是中间产物,你真正需要沉淀的是可复用的角色图、场景图和道具图。很多教程教你先出图再想名字,结果素材库十天就乱成一锅粥。我这边固定用下面这套目录结构,命名规则是「对象_视角_版本」,所有文件放进 assets 根目录,后面脚本校验时直接按这个规则查。
assets/ ├── characters/ │ ├── 阿桔_正面_竖版_v1.png │ ├── 阿桔_侧面_竖版_v1.png │ └── 阿桔_半身_竖版_v1.png ├── scenes/ │ └── 老街巷口_黄昏_v1.png ├── props/ │ └── 小卖部招牌_特写_v1.png └── styles/ └── 赛璐璐_风格参考_v1.png命名规范的三个约定:第一段是资产主人,角色、场景、道具各归各的文件夹;第二段是视角或时间,同一个场景的白天/黄昏版本必须分文件,因为光照会直接影响视频生成的调色;第三段是版本号,改版只加版本号,不覆盖旧文件,方便回溯。这套约定成本极低,但能让你在跑完五十个镜头后还找得着每一张图的出处。
4.2 角色一致性的三个常用方案对比
图片资产的核心价值就是喂给后续视频生成,角色一致性靠的不只是提示词,而是资产方案本身。我把常见的三个方案列个对比,你按项目体量选:
| 方案 | 做法 | 优点 | 代价 |
|---|---|---|---|
| 固定角色描述词 | 资产库里只维护一段文本 | 零成本,改词方便 | 一致性最弱,不同分镜还得碰运气 |
| 参考图 + IPAdapter | 选一张正脸图,每次生成都带参考 | 一致性较强,动画风格够用 | 需要熟悉 ComfyUI 节点,跑批略慢 |
| 多视图角色卡 | 生成正面/侧面/背面三张,组合成角色卡 | 一致性最强,适合多集连续剧 | 建卡成本高,每集要做二次校验 |
漫剧这种单集时长短、角色数量少的场景,我建议直接上方案二「参考图 + IPAdapter」,性价比最高。如果你的项目是几十集的连续漫剧,主角出场频次极高,才值得为方案三花时间。方案一的坑在于:提示词写「琥珀色眼睛、缺口耳朵」写得再细,模型出五张图大概率还是五张脸。
4.3 批量整理素材的脚本思路
分镜多了以后,手动检查每个镜头缺哪张图会疯。我写了个小脚本,直接读 storyboard.json,自动比对 assets 目录,输出一张「待补齐清单」。这样在进入 Seedance 生成之前,你能一眼看到哪些镜头缺角色图、哪些场景没对应资产,而不是等生成完才发现某张首帧是坏的。
import json import os from pathlib import Path storyboard = json.load(open("storyboard.json", encoding="utf-8")) assets_root = Path("assets") missing = [] for scene in storyboard["scenes"]: location = scene.get("location", "") # 场景图检查:注意 assets 里用下划线,剧本里可能是中文逗号 scene_file = assets_root / "scenes" / f"{location.replace(',', '_')}_黄昏_v1.png" if not scene_file.exists(): missing.append(f"场景图缺失: {location}") for shot in scene["shots"]: shot_no = shot.get("shot_no", "") if not (assets_root / "characters").glob("*"): missing.append(f"角色资产目录为空,无法匹配 {shot_no}") break report = "\n".join(missing) if missing else "资产齐全,可以进入视频生成阶段" print(report) with open("asset_check_report.txt", "w", encoding="utf-8") as fp: fp.write(report)逻辑说明:脚本遍历 storyboard 里的每个场景和镜头,按命名规范拼出预期文件名,用exists()检查资产是否存在。location 里如果带中文逗号,先替换成下划线再拼接,这是命名规范里最容易踩的细节。最后把缺失项写成文本报告,而不是直接抛异常,方便你批量补齐后重跑。
参数说明:assets_root指向你的资产根目录,如果你改了目录结构,改这一行就能适配。replace(',', '_')是必要的脏数据兜底,因为剧本里写「老街巷口,黄昏」和资产文件名里的下划线永远对不上,这类问题在脚本里静默处理,比回头人工排查效率高。这个检查脚本跑完,asset_check_report.txt 里没有内容,我再放心进 Seedance。
5. Seedance × 火山方舟 视频生成工作流:搭建步骤与避坑要点
5.1 工作流的整体形态:分镜图作为首帧,视频生成只做「动起来」
到了这一步,前面所有工作的目标都是让 Seedance 把分镜底图「动起来」。Seedance 是火山方舟上以模型服务方式提供的视频生成能力,刚才的静态分镜图会作为 first_frame 首帧输入,模型基于它和 prompt 生成后续视频帧。这里的关键认知是:视频生成阶段不要再试图改变画面内容,它只负责运动、镜头和时序。你在 prompt 里写新的角色描述、场景描述,都会和首帧打架,出来的画面不是鬼畜就是突变。
所以我建议完整链路是:分镜底图 → base64 编码 → 作为 first_frame 提交 → Seedance 根据 motion prompt 生成视频 → 下载成片。这也意味着分镜底图的质量就是视频质量的上限,第 3 章的工作在这里兑现。motion prompt 只写「动作 + 运镜」,不写任何外观描述词。
5.2 调 Seedance 的代码骨架:请求参数与轮询状态
调用火山方舟 Seedance 的方式和大多数模型服务类似,提交生成任务后轮询状态。下面这版代码我加了完整的注释和 base64 处理,你把它里的 API Key 和接入点地址替换成自己在火山方舟控制台申请到的值就能跑。
import base64 import time import requests API_KEY = "你的火山方舟 API Key" ENDPOINT = "你在方舟控制台创建的接入点地址" # 开通 Seedance 后自动生成 headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } # 读分镜底图并编码 with open("S1-01.png", "rb") as fp: img_b64 = base64.b64encode(fp.read()).decode() payload = { "model": "seedance 系列模型标识,以你控制台开通的服务名为准", "content": [ { "type": "video_generation", "video_generation": { "prompt": "猫站起身,缓推镜头,尾巴扫过瓦片,跳向亮灯的小卖部招牌", "first_frame": f"data:image/png;base64,{img_b64}", "resolution": "1080x1920", "duration": 5, "fps": 30, "seed": 20240601, }, } ], } resp = requests.post(ENDPOINT, json=payload, headers=headers) task = resp.json() task_id = task.get("id") or task.get("task_id") print("任务 ID:", task_id) # 任务提交后异步执行,轮询结果 while True: detail = requests.get(f"{ENDPOINT}/{task_id}", headers=headers).json() status = detail.get("status") print("当前状态:", status) if status in ("succeed", "failed"): break time.sleep(3) if status == "succeed": video_url = detail.get("video_url") or detail["output"]["video_url"] print("成片地址:", video_url) else: print("生成失败,错误信息:", detail.get("error") or detail)逻辑说明:提交阶段把任务id拿到,然后每隔 3 秒轮询一次状态,直到 succeed 或 failed。长视频生成要几十秒,轮询间隔太短会平白增加请求次数和费用;3 秒是平衡值。status和返回内容的字段名在不同版本接口里略有差异,所以代码里给了or的兜底写法。
参数说明:first_frame我用了data:image/png;base64,{img_b64}前缀,这是图生视频常见的传输格式,少了前缀会被当成纯文本解析而报错。duration注意别超过模型上限,Seedance 单次生成时长受分辨率档位限制,5 秒是稳妥值。seed固定能降低同一组镜头间的随机性,但不同任务的 seed 不必连续,用一个有意义的种子号即可。resolution对齐 1080x1920 竖版档位,和第 3 章分镜底图保持一致,避免首帧拉伸。
5.3 高频翻车点排查:现象、原因、解决
视频生成阶段的问题不像代码报错那么直接,很多是画面层面的,只能靠经验判断。我把自己踩过的坑整理成下面五条,按「现象 → 原因 → 解决」的结构写,每条都是真实发生过、改完再跑就正常的。
坑一:首帧失效,角色突然变脸现象:明明提交了分镜底图,生成出来的第一秒角色脸型、发色和底图不一致。 原因:first_frame 字段名传错,或者 base64 前缀缺了,服务端没解析到图片,等于跑了文生视频。 解决:先打印提交给服务端的 payload,确认 first_frame 以data:image/png;base64,开头;若没有,就是 encode 环节丢了前缀。代码里我已写好完整写法,直接对照排查。
坑二:镜头运动像幻灯片,画面几乎不动现象:视频能出,但主体只平移了一下就定住,没有自然的动作变化。 原因:motion prompt 里塞了太多动作,「起跳、扑向、回头看」全挤在一起,模型只能挑一个完成。 解决:一个镜头只写一个主动作加一个简单运镜。比如「猫从蹲姿缓缓站起来,镜头缓慢推进」。粗糙的动作描述远胜于堆砌,因为模型分不清动作优先级。
坑三:任务报错提示参数非法现象:提交后立刻返回 4xx 错误,提示 duration、resolution 或 model 字段有问题。 原因:多数是对齐问题——duration 超过了当前档位上限,或分辨率不是模型支持的档位,或 model 标识复制错了。 解决:打开火山方舟控制台,对照你开通的服务说明逐项核对字段取值。别自作聪明传 8 秒时长或 2K 分辨率,只有文档里明确列出的档位才合法。不同模型(比如 Minimax 或可灵)参数不通用,换模型必须重新核对。
坑四:画面里出现乱码文字现象:生成结果里招牌、海报上的文字全是变形乱码,特别显眼。 原因:分镜底图本身带了小字,视频生成时模型尝试重建文字但失败了。 解决:在分镜阶段就处理掉文字内容,把招牌改成纯色图案,或者把文字移出画面。这个坑在后期几乎没法修,只能重做首帧,返工成本极高。
坑五:耗时长、费用超出预期现象:同一批镜头反复重试,跑的时长和费用比预算翻了一倍。 原因:没有先在低分辨率档位上试跑 motion prompt,直接上 1080x1920 终份档,失败一次就烧一次钱。 解决:先拿 1~2 个镜头用最低档分辨率跑通 motion prompt,确认动作自然、首帧对齐后再批量提交终档。这套工具包的工作流里也内置了「草稿档验证」这一步,别跳过。
这些坑的共同点在于:问题大多出在提交前的数据准备,而不是模型本身。5.2 的代码骨架和 5.3 的排查表搭配使用,能覆盖九成以上的失败场景。
6. 进阶技巧:成本核算、批量输出与成片一致性校验
6.1 成本怎么算:按秒计费与前期的「钞能力」控制
Seedance 这类视频模型的计费逻辑是按生成时长和分辨率档位算的,同一段 5 秒视频,1080p 的单价明显高于低档位。我现在的习惯是:任何新项目先做两件事,一是估算总时长,二是把批量提交前需要验证的镜头控制在两三个以内。总成本的大头从来不是正常生成,而是失败重试——首帧不对、动作鬼畜、时长超限,每失败一次烧掉的时间和费用都白费。所以「草稿档验证 + 参数核对 + 资产检查」这三步前置工作做扎实,省下来的费用远大于多做几分钟测试。控制台里的用量统计记得定时看,我见过到了月底才发现超支的例子不止一个。
6.2 成片一致性校验:抽帧比对 + 场景打点
视频批量跑完不能直接发,至少做一次成片与分镜的一致性抽检。我的做法是从每段成片里抽首帧和中段帧,和分镜底图做一次相似度粗筛,把偏差大的标出来人工复核。脚本逻辑很简单,但能筛掉大部分首帧错位的漏网之鱼。
import cv2 video = cv2.VideoCapture("S1-01.mp4") ok, frame = video.read() # 第一帧 storyboard = cv2.imread("S1-01.png") # 统一尺寸后做直方图比较 frame_small = cv2.resize(frame, (256, 256)) board_small = cv2.resize(storyboard, (256, 256)) score = cv2.compareHist( cv2.calcHist([frame_small], [0], None, [256], [0, 256]), cv2.calcHist([board_small], [0], None, [256], [0, 256]), cv2.HISTCMP_CORREL, ) print("首帧相似度:", score) # score 低于 0.6 的镜头标记复查 if score < 0.6: print("S1-01 首帧偏差较大,需人工复核")逻辑说明:compareHist算的是两帧直方图的相似度,对色调和整体构图变化敏感,对局部细节变化不敏感。这正好适合做粗筛——首帧如果完全对不上,分数会非常低;画面构图相同但颜色有轻微漂移,分数会正常。细看阶段的镜头,按 shot_no 找到对应的 storyboard 手动比对即可。
这个校验脚本看着简单,但它挡住的都是发布后才发现的大问题。从那以后,我每次批量生成完都强制走一遍抽帧比对,阈值卡在 0.6,宁可多花十分钟筛出有问题的镜头重新补一条,也不愿整批项目交付后才发现首帧崩了。这套工具包把第 2 到第 5 章说的完整流程收成了开箱即用的工程文件,你拿自己的剧本替换输入,按序跑一遍就能复现整个链路,希望帮到你。
本文还有配套的精品资源,点击获取