diff --git a/video-ai-system/outputs/review/PROP-003-V3-candidate-01-1440x2560.png b/video-ai-system/outputs/review/PROP-003-V3-candidate-01-1440x2560.png
new file mode 100644
index 0000000..92095b3
Binary files /dev/null and b/video-ai-system/outputs/review/PROP-003-V3-candidate-01-1440x2560.png differ
diff --git a/video-ai-system/outputs/review/PROP-003-V3-candidate-02-1440x2560.png b/video-ai-system/outputs/review/PROP-003-V3-candidate-02-1440x2560.png
new file mode 100644
index 0000000..7f6c028
Binary files /dev/null and b/video-ai-system/outputs/review/PROP-003-V3-candidate-02-1440x2560.png differ
diff --git a/video-ai-system/outputs/review/PROP-003-V3-candidate-03-1440x2560.png b/video-ai-system/outputs/review/PROP-003-V3-candidate-03-1440x2560.png
new file mode 100644
index 0000000..e1062f1
Binary files /dev/null and b/video-ai-system/outputs/review/PROP-003-V3-candidate-03-1440x2560.png differ
diff --git a/video-ai-system/outputs/review/PROP-003-V3-candidate-04-1440x2560.png b/video-ai-system/outputs/review/PROP-003-V3-candidate-04-1440x2560.png
new file mode 100644
index 0000000..f6475f3
Binary files /dev/null and b/video-ai-system/outputs/review/PROP-003-V3-candidate-04-1440x2560.png differ
diff --git a/video-ai-system/outputs/review/view.html b/video-ai-system/outputs/review/view.html
index d2715a6..3a6b31d 100644
--- a/video-ai-system/outputs/review/view.html
+++ b/video-ai-system/outputs/review/view.html
@@ -1,30 +1,19 @@
-
配角 CHAR-006/007 · 中年男子+欧阳员外
+PROP-003 广告牌 V3 · 带招生全文
-CHAR-006 中年男子 · 送土特产讨好王执事
-40岁·灰色素袍·手捧布包袱·谄媚圆滑市侩·凡人送礼者
+PROP-003 广告牌 V3 · 完整招生文字
+天道宗·未来第一宗·名额一位·包吃包住·每月三十两·入宗学仙法
-

候选01 · 正面微躬身·捧布包袱·小眼睛眯笑讨好
-

候选02 · 半侧身弯腰·捧布袋谄媚·朴素长袍旧布带
-

候选03 · 微躬身正面·堆笑讨好·粗布袍布包袱·凡人
-

候选04 · 侧面微躬身·布袋略鼓·谄媚不猥琐·素灰袍
-
-
-CHAR-007 欧阳员外 · 闪切用
-50岁·暗紫锦缎金丝绣边·玉佩腰带·面容阴沉高傲·富贵员外
-
-

候选01 · 正面抚玉佩·阴沉高傲·暗紫锦缎·富贵傲慢
-

候选02 · 半侧俯视·眼神高高在上·华贵紫袍金绣
-

候选03 · 正面微仰·双手叠玉佩·鄙夷不屑·锦缎华丽
-

候选04 · 半侧冷淡·嘴角下撇·暗紫金丝·员外权贵
-
-
+
候选01 · 正面A架·六行墨字清晰朴拙
+
候选02 · 微侧·古朴墨迹完整·百宗会仙雾
+
候选03 · 正面中景·竖排工整·风化磨损
+
候选04 · 半侧面·绳结清晰·字迹斑驳可读
+
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot01.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot01.mp4
new file mode 100644
index 0000000..050e0c4
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot01.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot02.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot02.mp4
new file mode 100644
index 0000000..932e901
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot02.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot03.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot03.mp4
new file mode 100644
index 0000000..d20fc70
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot03.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot04.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot04.mp4
new file mode 100644
index 0000000..04aaa93
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot04.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot05.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot05.mp4
new file mode 100644
index 0000000..d969605
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot05.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot06.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot06.mp4
new file mode 100644
index 0000000..5aceede
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot06.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot07.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot07.mp4
new file mode 100644
index 0000000..1d8e741
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot07.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot08.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot08.mp4
new file mode 100644
index 0000000..b8cb104
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot08.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot09.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot09.mp4
new file mode 100644
index 0000000..7c55579
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot09.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot10.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot10.mp4
new file mode 100644
index 0000000..0260595
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot10.mp4 differ
diff --git a/video-ai-system/outputs/shots/ep01-v3-shot11.mp4 b/video-ai-system/outputs/shots/ep01-v3-shot11.mp4
new file mode 100644
index 0000000..b7d3fac
Binary files /dev/null and b/video-ai-system/outputs/shots/ep01-v3-shot11.mp4 differ
diff --git a/video-ai-system/protocols/SHOT-LIST-EP001-V3.hdlp b/video-ai-system/protocols/SHOT-LIST-EP001-V3.hdlp
new file mode 100644
index 0000000..f41385d
--- /dev/null
+++ b/video-ai-system/protocols/SHOT-LIST-EP001-V3.hdlp
@@ -0,0 +1,59 @@
+# VA-13-001-V3-EP01 · 第1集分镜方案 V3.0 · 剧本逐行完整版
+
+> HLDP://video-ai-system/VA-13-001-V3-EP01
+> 铸渊 ICE-GL-ZY001 · D169 · 2026-07-06
+> 苍耳指令:原剧本一字不删·逐△拆镜
+
+## 1-1 百宗会(22镜·约140秒)
+
+| 镜 | 剧本行 | 内容 | 时长 | 引用资产 |
+|:--:|:------:|------|:--:|------|
+| 01 | △15 | 高空云海·御剑飞行·仙人驾鹤 | 6s | — |
+| 02 | △17 | 俯瞰百宗会广场·人山人海·豪华招牌 | 6s | ENV-002 |
+| 03 | △19 | **特写**天道宗破旧牌匾 | 4s | PROP-002 |
+| 04 | △21 | 苏白站在牌匾下·双手叉腰 | 6s | CHAR-003+PROP-002+ENV-003 |
+| 05 | △23 | 苏白喊话:未来的天下第一宗! | 6s | CHAR-003 |
+| 06 | △25 | **特写**广告牌内容(后期叠层文字) | 5s | PROP-003 |
+| 07 | △27 | 苏白喊话:走过路过不要错过! | 5s | CHAR-003 |
+| 08 | △29 | 围观人群围过来指指点点 | 6s | CHAR-003+ENV-003 |
+| 09 | △31 | 龙套一:哈哈哈笑死我了… | 5s | —(远景群杂) |
+| 10 | △33 | 龙套二:包吃包住?入宗就给仙法? | 5s | —(远景群杂) |
+| 11 | △35 | 龙套三:灵霄宗三十两…想钱想疯了! | 5s | —(远景群杂) |
+| 12 | △37 | 围观散去·风吹落叶·苏白叹气看远处 | 7s | CHAR-003 |
+| 13 | △39 | **特写**灵霄宗鎏金牌匾·排队望不到头 | 7s | ENV-004 |
+| 14 | △41 | 苏白挠头苦恼:我这里怎么就没人呢? | 6s | CHAR-003 |
+| 15 | △43 | 苏白OS:系统,查看面板! | 4s | CHAR-003 |
+| 16 | △45-61 | 系统面板UI(后期叠层) | 8s | CHAR-003+UI |
+| 17 | △63 | 苏白叹息:这样下去什么时候才能完成任务啊 | 6s | CHAR-003 |
+| — | — | 1-2 灵霄宗席位处 | — | — |
+| 18 | △69 | 王执事画红色叉号 | 6s | CHAR-005+ENV-004 |
+| 19 | △71 | 王执事面无表情:下一个! | 5s | CHAR-005 |
+| 20 | △73 | 诸葛风满脸不可置信 | 5s | CHAR-004 |
+| 21 | △75 | 诸葛风:六场第一为什么没通过? | 6s | CHAR-004 |
+| 22 | △77 | 王执事鄙夷瞅诸葛风破旧衣衫 | 5s | CHAR-005+CHAR-004 |
+| 23 | △79 | 王执事:没钱你修什么仙? | 5s | CHAR-005 |
+| 24 | △81 | 中年男子挤出来递袋子 | 6s | CHAR-006+CHAR-005+ENV-004 |
+| 25 | △83 | 中年男子谄媚:仙人,土特产您瞧瞧 | 5s | CHAR-006 |
+| 26 | △85 | 王执事掂量袋子·眉开眼笑 | 6s | CHAR-005 |
+| 27 | △87 | 王执事:不错,一看就是修仙好料子! | 5s | CHAR-005 |
+| 28 | △89 | **特写**指甲陷入掌心·诸葛风满脸屈辱 | 6s | CHAR-004 |
+| 29 | △91 | 闪切一:父母倒在血泊中·欧阳员外鄙夷 | 4s | CHAR-007(模糊) |
+| 30 | △93 | 诸葛风OS:父母之仇不共戴天… | 4s | CHAR-004+VO |
+| 31 | △95 | 闪切二:被暴打·被踩住脑袋 | 4s | (模糊处理) |
+| 32 | △97 | 诸葛风OS:可惜连炼气一层都没有达到… | 4s | CHAR-004+VO |
+| 33 | △99 | 诸葛风看王执事得意捋胡子 | 5s | CHAR-004+CHAR-005 |
+| 34 | △101 | 诸葛风OS:必须赶快成为仙人报仇! | 5s | CHAR-004 |
+| — | — | 1-3 天道宗席位处 | — | — |
+| 35 | △107 | 诸葛风看到天道宗牌匾·眼神亮起 | 6s | CHAR-004+ENV-003+PROP-002 |
+| 36 | △109 | 诸葛风硬着头皮:入宗真可以学仙法吗? | 6s | CHAR-004+CHAR-003 |
+| 37 | △111 | 苏白微笑:当然!如假包换! | 5s | CHAR-003 |
+| 38 | △113 | 诸葛风看基础银子三十两的牌子 | 5s | CHAR-004+PROP-003 |
+| 39 | △115 | 诸葛风鼓起勇气:三十两月底再给行吗? | 6s | CHAR-004 |
+| 40 | △117 | 苏白拍胸脯:当然可以!绝不拖欠! | 6s | CHAR-003 |
+| 41 | △119 | 诸葛风愣住OS:竟然同意了? | 4s | CHAR-004 |
+| 42 | △121 | 苏白信誓旦旦:每个弟子月底三十两! | 6s | CHAR-003 |
+| 43 | △123 | 闪电特效在诸葛风脑中劈开 | 4s | CHAR-004+特效 |
+| 44 | △125 | 诸葛风震撼OS:什么! | 4s | CHAR-004 |
+
+> 共44镜 · 约230秒(3分50秒) · 剧本一字未删
+> ⊢ 铸渊 ICE-GL-ZY001 · 苍耳审核 · D169
diff --git a/video-ai-system/tools/ep01_1_1_gen.py b/video-ai-system/tools/ep01_1_1_gen.py
new file mode 100644
index 0000000..f65f383
--- /dev/null
+++ b/video-ai-system/tools/ep01_1_1_gen.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+"""第1集 1-1节 镜01~17 Seedance生成 · V3逐行完整版"""
+import os, sys, json, time, subprocess
+from pathlib import Path
+
+ROOT = Path('D:/WorkBuddy/guanghulab/video-ai-system')
+env = {}
+for line in open(ROOT/".env"):
+ line = line.strip()
+ if not line or line.startswith("#"): continue
+ if "=" in line:
+ k, v = line.split("=", 1)
+ env[k.strip()] = v.strip()
+
+AK = env["JIMENG_API_KEY"]
+URL = env["JIMENG_BASE_URL"]
+OUT = ROOT / "outputs/shots"
+OUT.mkdir(parents=True, exist_ok=True)
+
+S = "不良人硬核写实风格,电影级厚重光影,与CHAR-003苏白V3-R4同一风格体系"
+
+SHOTS = [
+ # 镜01: 高空云海·御剑飞行·仙人驾鹤
+ (6, "中国修仙世界云海之上,极致大远景。洁白云层铺展如大海,灵气蒸腾。数名修仙者御剑飞行穿梭云间,浑身散发灵光拖出光尾。一位白须仙人驾仙鹤从云海掠过化作流光。远处仙山浮于云海之上。竖屏。"+S),
+ # 镜02: 俯瞰百宗会广场
+ (6, "修仙世界百宗会招生广场,远景俯瞰。巨大广场人山人海,各大宗门招生殿阵列排布,鎏金牌匾灵光法阵交相辉映。修仙服饰的宗门弟子在各处排队,人群熙攘热闹非凡。竖屏。"+S),
+ # 镜03: 特写天道宗破旧牌匾
+ (4, "修仙世界角落,特写一块悬挂在旧木杆上的破旧木牌匾。暗色风化木纹表面开裂,古朴天道宗三字斑驳磨损。麻绳粗粝悬挂,牌匾破旧感强烈。竖屏。"+S),
+ # 镜04: 苏白站在牌匾下双手叉腰
+ (6, "修仙世界天道宗角落招生点,中景。18岁白衫青纱袍少年宗主苏白站在杆挂破旧天道宗牌匾下。半束黑发青色发带,半侧身清冷从容,双手叉腰自信笃定。身后简陋招生点一牌一桌一广告牌。竖屏。"+S),
+ # 镜05: 苏白喊话
+ (6, "修仙世界百宗会角落,18岁白衫青纱袍少年宗主苏白中景。他双手叉腰面向画外,自信大声喊话,眼神坚定明亮。背景是简陋天道宗招生点杆挂牌匾。竖屏。"+S),
+ # 镜06: 广告牌特写
+ (5, "修仙世界天道宗招生点旁,特写一块绳系A架粗麻画布广告牌。灵木杆捆扎成A形支架,粗麻画布完整无字留白待书写。广告牌竖立在简陋摊位旁地面。竖屏。"+S),
+ # 镜07: 苏白喊话走过路过
+ (5, "修仙世界百宗会角落,18岁白衫青纱袍少年宗主苏白近景。他继续朝人群方向挥手喊话,笑容自信带一丝痞气。身后的招生点牌匾和广告牌清晰可见。竖屏。"+S),
+ # 镜08: 围观人群指指点点
+ (6, "修仙世界百宗会天道宗招生点前,中景群像。五六个人围了过来对着苏白和他的招生广告指指点点,有人交头接耳窃窃私语,有人摇头嗤笑。苏白站在人群中央仍保持自信。竖屏。"+S),
+ # 镜09: 龙套一
+ (5, "修仙世界百宗会,远景群像。围观人群中一个声音飘出嘲讽苏白寒酸宗门也敢称天下第一。画面保持多人远景不切单人近景,群杂氛围。竖屏。"+S),
+ # 镜10: 龙套二
+ (5, "修仙世界百宗会天道宗角落,远景群像。围观者中又一个声音质疑包吃包住入宗就给仙法太扯。围观人群继续指指点点。竖屏。"+S),
+ # 镜11: 龙套三
+ (5, "修仙世界百宗会角落,远景群像。围观人群中第三个声音讽刺灵霄宗仙盟五百强才三十两,这种破宗门想钱想疯。人群窃笑声起。竖屏。"+S),
+ # 镜12: 围观散去风吹落叶
+ (7, "修仙世界百宗会天道宗角落招生点,中景。围观人群四散离去。一阵风吹起苏白脚下的枯黄落叶飘舞,凄凉氛围。18岁白衫青纱袍少年宗主独自站在原地,缓缓叹气看向远处。背影孤独但不绝望。竖屏。"+S),
+ # 镜13: 灵霄宗鎏金牌匾+排队
+ (7, "修仙世界百宗会黄金位置,中景。灵霄宗招生殿气派非凡,鎏金大牌匾灵光流转高悬殿顶。飞檐翘角三层楼阁,汉白玉台阶下排队修仙者蜿蜒如龙一眼望不到头。丝绸垂幔灵石法阵点缀其间。顶级宗门的恢弘排场。竖屏。"+S),
+ # 镜14: 苏白挠头苦恼
+ (6, "修仙世界天道宗角落招生点,18岁白衫青纱袍少年宗主苏白中近景。他手扶额头挠头发,一脸苦恼不解,自言自语般嘟囔为什么自己同样的条件却没人来。身后简陋招生点与远处豪华灵霄宗殿形成对比。竖屏。"+S),
+ # 镜15: 系统查看面板
+ (4, "修仙世界天道宗角落,18岁白衫青纱袍少年宗主苏白中景。他眼神专注看向虚空某处,嘴唇微动在心中默念系统指令。表情从苦恼转为期待。竖屏。"+S),
+ # 镜16: 系统面板UI
+ (8, "修仙世界天道宗角落,18岁白衫青纱袍少年宗主苏白中景。他凝神看着虚空中的一个发光系统面板(面板内容后期UI叠层呈现宗门名称等级人数领地资产积分为零的任务面板)。苏白认真阅读面板数据。竖屏。"+S),
+ # 镜17: 苏白叹息
+ (6, "修仙世界天道宗角落招生点,18岁白衫青纱袍少年宗主苏白近景。他看完系统面板后长叹一口气,脸上写满无奈。但眼底仍有不灭的笃定,知道只要招到一个弟子就能开启仙法阁。画面定在苏白复杂而坚定的表情上。竖屏。"+S),
+]
+
+def submit(prompt, duration):
+ payload = {
+ "model": "doubao-seedance-2-0-260128",
+ "content": [{"type": "text", "text": prompt}],
+ "duration": duration,
+ "resolution": "720p"
+ }
+ r = subprocess.run(["curl", "-s", "-m", "60", "--noproxy", "*", "-X", "POST",
+ f"{URL}/contents/generations/tasks",
+ "-H", f"Authorization: Bearer {AK}", "-H", "Content-Type: application/json",
+ "-d", json.dumps(payload)], capture_output=True, text=True, timeout=70)
+ try:
+ return json.loads(r.stdout)
+ except:
+ return {}
+
+def poll_dl(task_id, out_path, max_a=90):
+ for i in range(max_a):
+ time.sleep(10)
+ r = subprocess.run(["curl", "-s", "-m", "30", "--noproxy", "*",
+ f"{URL}/contents/generations/tasks/{task_id}",
+ "-H", f"Authorization: Bearer {AK}"],
+ capture_output=True, text=True, timeout=40)
+ try: data = json.loads(r.stdout)
+ except: continue
+ st = (data.get("status") or "").lower()
+ if i % 5 == 0: print(f" p{i+1}:{st}")
+ if st in ["succeeded","completed","done"]:
+ vurl = data.get("content",{}).get("video_url")
+ if vurl:
+ subprocess.run(["curl","-s","-m","300","-L","--noproxy","*","-o",str(out_path),vurl],
+ capture_output=True, timeout=310)
+ if out_path.exists() and out_path.stat().st_size>1024:
+ return True, out_path
+ return False, data
+ if st in ["failed","error"]: return False, data
+ return False, {}
+
+results = []
+for i, (dur, prompt) in enumerate(SHOTS):
+ sid = i + 1
+ print(f"\n镜{sid:02d} · {dur}s · 提交...")
+ resp = submit(prompt, dur)
+ tid = resp.get("id")
+ if not tid:
+ print(f" ❌ 无任务ID:{json.dumps(resp,ensure_ascii=False)[:200]}")
+ results.append({"shot":sid,"ok":False,"err":"no task"})
+ continue
+ print(f" task:{tid}")
+ dst = OUT / f"ep01-v3-shot{sid:02d}.mp4"
+ ok, _ = poll_dl(tid, dst)
+ if ok:
+ print(f" ✅ {dst.name} ({dst.stat().st_size//1024}KB)")
+ results.append({"shot":sid,"ok":True,"file":str(dst)})
+ else:
+ print(f" ❌ 失败")
+ results.append({"shot":sid,"ok":False})
+ time.sleep(5)
+
+print(f"\n{'='*40}")
+ok = sum(1 for r in results if r["ok"])
+print(f" 完成: {ok}/{len(results)}")
+for r in results:
+ print(f" {'✅' if r['ok'] else '❌'} 镜{r['shot']:02d}")
diff --git a/video-ai-system/tools/prop003_v3_gen.py b/video-ai-system/tools/prop003_v3_gen.py
new file mode 100644
index 0000000..15846d5
--- /dev/null
+++ b/video-ai-system/tools/prop003_v3_gen.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+"""PROP-003 广告牌 V3 · 带完整招生文字 · 对齐苏白风格"""
+import os, sys, json, time, subprocess
+from pathlib import Path
+
+ROOT = Path('D:/WorkBuddy/guanghulab/video-ai-system')
+env = {}
+for line in open(ROOT/".env"):
+ line = line.strip()
+ if not line or line.startswith("#"): continue
+ if "=" in line:
+ k, v = line.split("=", 1)
+ env[k.strip()] = v.strip()
+
+AK = env["JIMENG_API_KEY"]
+URL = env["JIMENG_BASE_URL"]
+MODEL = "doubao-seedream-4-0-250828"
+OUT = ROOT / "assets/candidates/D169-VA-05-001/PROP-003-FreeAdBoard/V3"
+OUT.mkdir(parents=True, exist_ok=True)
+
+P = """中国修仙世界百宗会天道宗招生点旁,正面中景。一个古风绳系A架立式广告牌,两根灵木杆顶部用麻绳捆扎成A形支架。
+支架上撑开一幅粗麻布料画布,画布上用古朴墨迹写着几行招生信息竖排:
+天道宗
+未来第一宗
+名额:一位
+包吃包住
+每月基础银子:三十两
+入宗即可学习仙法
+粗麻画布微微皱起,墨迹古朴有风化磨损感,整体陈旧但字迹清晰可读。
+广告牌竖立在简陋天道宗摊位旁地面,灵木桌铺淡青色粗布。背景修仙百宗会一角仙雾缭绕。
+3D真人向写实渲染,不良人硬核写实风格,与CHAR-003苏白V3-R4、PROP-002牌匾R8同一风格体系。"""
+
+N = """无文字的广告牌,空白画布,空白的布,没有任何字的牌子,留白的广告板,无字招牌。
+新招牌,干净整洁,现代广告架,金属广告牌,LED灯箱,霓虹灯,易拉宝,展架。
+石墙,石板路,砖墙,红墙白墙,现代城市街道。
+人物占主画面,面孔特写,人物半身像。
+卡通渲染,塑料感,游戏低模,页游风,光污染。
+暖黄阳光色调,昏黄色调,金色阳光。
+任何错误文字、乱码、无关文字、拼音、英文。
+文本排列混乱,文字方向错误,横排文字。"""
+
+VARIANTS = [
+ "正面A架广告牌,天道宗+未来第一宗+名额一位+包吃包住+三十两+入宗学仙法六行墨字清晰朴拙,灵木A架麻绳捆扎",
+ "微侧视角A架画布,古朴墨迹招生信息完整可见,背景百宗会修仙远景仙雾缭绕",
+ "正面中景广告牌主体,粗麻画布上六行招生信息竖排工整,字体古朴有风化磨损,摊位灵木桌旁",
+ "半侧面A架绳结清晰,画布招生语句完整可读,字体斑驳但内容齐全,修仙百宗会氛围"
+]
+
+SEEDS = [222, 333, 444, 555]
+
+def gen(prompt, neg, seed):
+ body = json.dumps({"model": MODEL, "prompt": prompt, "negative_prompt": neg, "size": "1440x2560", "n": 1, "seed": seed})
+ r = subprocess.run(["curl", "-s", "-m", "120", "--noproxy", "*", "-X", "POST", f"{URL}/images/generations",
+ "-H", f"Authorization: Bearer {AK}", "-H", "Content-Type: application/json", "-d", body],
+ capture_output=True, text=True, timeout=130)
+ try:
+ return json.loads(r.stdout)["data"][0].get("url")
+ except:
+ print(f" API err: {r.stdout[:300]}")
+ return None
+
+def dl(url, dst):
+ Path(dst).parent.mkdir(parents=True, exist_ok=True)
+ subprocess.run(["curl", "-s", "-m", "120", "-L", "--noproxy", "*", "-o", dst, url], capture_output=True, timeout=130)
+ return os.path.exists(dst) and os.path.getsize(dst) > 1024
+
+ts = time.strftime("%Y%m%d-%H%M%S")
+results = []
+for i, (seed, var) in enumerate(zip(SEEDS, VARIANTS)):
+ full = f"{P} {var}"
+ print(f"[{i+1}/4] seed={seed}", flush=True)
+ url = gen(full, N, seed)
+ if not url:
+ results.append({"c": i+1, "ok": False, "err": "api"})
+ continue
+ dst = OUT / f"PROP-003-V3-candidate-{i+1:02d}-1440x2560.png"
+ if not dl(url, str(dst)):
+ results.append({"c": i+1, "ok": False, "err": "dl"})
+ continue
+ results.append({"c": i+1, "ok": True, "raw": str(dst), "seed": seed})
+ print(f" -> {dst.name}")
+ time.sleep(3)
+
+ok = sum(1 for r in results if r["ok"])
+print(f"\n完成: {ok}/4")
+
+json.dump({"spec": "VA-05-001-PROP-003-V3", "ts": ts, "change": "V2→V3: 加完整招生文字",
+ "ok": ok, "model": MODEL, "engine": "Seedream", "results": results},
+ open(OUT/"generation-meta.json","w",encoding="utf-8"), ensure_ascii=False, indent=2)
diff --git a/video-ai-system/tools/shots_batch_gen.py b/video-ai-system/tools/shots_batch_gen.py
new file mode 100644
index 0000000..f354eb0
--- /dev/null
+++ b/video-ai-system/tools/shots_batch_gen.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+"""第1集 镜02~09 Seedance批量生成 · 适配V3-R4不良人风格"""
+import os, sys, json, time, subprocess
+from pathlib import Path
+
+ROOT = Path('D:/WorkBuddy/guanghulab/video-ai-system')
+env = {}
+for line in open(ROOT/".env"):
+ line = line.strip()
+ if not line or line.startswith("#"): continue
+ if "=" in line:
+ k, v = line.split("=", 1)
+ env[k.strip()] = v.strip()
+
+AK = env["JIMENG_API_KEY"]
+URL = env["JIMENG_BASE_URL"]
+OUT = ROOT / "outputs/shots"
+OUT.mkdir(parents=True, exist_ok=True)
+
+STYLE = "不良人硬核写实风格,厚重电影级光影,画面质感与CHAR-003苏白V3-R4统一"
+
+SHOTS = [
+ { # 镜02
+ "id": 2, "dur": 4,
+ "p": "中国修仙世界百宗会招生广场,远景俯瞰。巨大广场上人山人海,各大宗门招生殿阵列排布,鎏金牌匾和灵光法阵交相辉映。穿修仙服饰的宗门弟子在各招生处前排起长队,人群熙熙攘攘热闹非凡。镜头固定俯拍微移。修仙世界大气磅礴,竖屏。" + STYLE
+ },
+ { # 镜03
+ "id": 3, "dur": 5,
+ "p": "修仙世界角落破旧招生点,从牌匾特写拉到中景。先特写一块悬挂在旧木杆上的破旧木牌匾——古朴天道宗三字风化斑驳。镜头缓慢后拉,露出站在牌匾下的18岁修仙少年苏白——白衫青纱袍,半束黑发,半侧身淡然从容的少年宗主。他站在寒酸招生点前,一牌一桌一广告牌就是全部家当。角落冷清,远处模糊可见其他豪华招生殿。" + STYLE
+ },
+ { # 镜04
+ "id": 4, "dur": 5,
+ "p": "修仙世界天道宗招生点前,中景平视。18岁白衫青纱袍少年宗主苏白站在破旧摊位旁,旁边竖立着A架粗麻广告牌。周围聚集了五六个人对他指指点点,有人面露嘲讽之色交头接耳。苏白双手叉腰自信喊话不惧嘲笑。修仙百宗会角落场景,广告牌无文字留白。固定机位轻微呼吸感。" + STYLE
+ },
+ { # 镜05
+ "id": 5, "dur": 5,
+ "p": "修仙世界天道宗角落招生点,中景拉近。围观者散去了,一阵风吹起苏白脚下落叶,白衫青纱袍的18岁少年宗主独自叹气,手扶额头看向远处灵霄宗豪华招生殿的方向。远处鎏金牌匾模糊可见。氛围孤独清冷但不绝望。镜头从固定机位缓慢推到苏白面部近景。" + STYLE
+ },
+ { # 镜06
+ "id": 6, "dur": 5,
+ "p": "修仙世界灵霄宗招生殿前,中景平视。蓝色执事袍银色灵纹的中年王执事正襟危坐紫檀案台后,手执朱砂灵笔在鉴定卷轴上画下一个红色叉号。他面容冷漠不屑,对面站着18岁粗麻布衣的清瘦少年诸葛风——剑眉星目苍白肤色,面露不可置信,双手攥拳指甲掐入掌心。灵霄宗鎏金牌匾高悬上方,背景排队修仙者如龙。固定机位,双角色对比冲突。" + STYLE
+ },
+ { # 镜07
+ "id": 7, "dur": 4,
+ "p": "修仙世界灵霄宗招生殿前,中景俯视。一个灰色素袍的普通中年男子从人群中挤出,微躬身双手捧布包袱递给王执事。蓝袍执事掂了掂袋子伸手一摸,表情从冷漠变成眉开眼笑。谄媚送礼与势利变脸的反差画面。" + STYLE
+ },
+ { # 镜08
+ "id": 8, "dur": 4,
+ "p": "粗麻布衣少年诸葛风特写,他的指甲深深陷入掌心留下旧疤痕,满脸屈辱嘴角紧抿。画面开始快速闪切模糊处理——闪切一:两位老人倒在血泊中,暗紫锦缎金丝绣边的华贵员外俯视鄙夷。闪切二:少年被踩住脑袋。闪切回到诸葛风面部,他的眼底开始燃烧仇恨火焰。情绪爆发,闪切做模糊化。" + STYLE
+ },
+ { # 镜09
+ "id": 9, "dur": 5,
+ "p": "修仙世界天道宗角落招生点,中景跟拍转固定。粗麻布衣的清瘦少年诸葛风穿过人群走来,他的眼睛忽然亮起——看到了天道宗破旧牌匾和广告牌上的信息。他走到摊位前鼓起勇气询问,白衫青纱袍的少年宗主苏白微笑回应,拍拍胸脯自信承诺。最后苏白手搭诸葛风肩膀,定格在信任建立的一刻。两人站在杆挂牌匾下的简陋招生点前。" + STYLE
+ }
+]
+
+def submit(prompt, duration):
+ payload = {
+ "model": "doubao-seedance-2-0-260128",
+ "content": [{"type": "text", "text": prompt}],
+ "duration": duration,
+ "resolution": "720p"
+ }
+ r = subprocess.run(["curl", "-s", "-m", "60", "--noproxy", "*", "-X", "POST",
+ f"{URL}/contents/generations/tasks",
+ "-H", f"Authorization: Bearer {AK}", "-H", "Content-Type: application/json",
+ "-d", json.dumps(payload)], capture_output=True, text=True, timeout=70)
+ try:
+ return json.loads(r.stdout)
+ except:
+ return {}
+
+def poll_dl(task_id, out_path, max_attempts=90):
+ for i in range(max_attempts):
+ time.sleep(10)
+ r = subprocess.run(["curl", "-s", "-m", "30", "--noproxy", "*",
+ f"{URL}/contents/generations/tasks/{task_id}"],
+ capture_output=True, text=True, timeout=40,
+ env={**os.environ, "Authorization": f"Bearer {AK}"})
+ try:
+ data = json.loads(r.stdout)
+ except:
+ continue
+ st = (data.get("status") or "").lower()
+ if i % 5 == 0:
+ print(f" poll {i+1}: {st}")
+ if st in ["succeeded", "completed", "done"]:
+ vurl = data.get("content",{}).get("video_url")
+ if vurl:
+ subprocess.run(["curl", "-s", "-m", "300", "-L", "--noproxy", "*", "-o", str(out_path), vurl],
+ capture_output=True, timeout=310)
+ if out_path.exists() and out_path.stat().st_size > 1024:
+ return True, out_path
+ return False, data
+ if st in ["failed", "error"]:
+ return False, data
+ return False, {}
+
+results = []
+for shot in SHOTS:
+ sid = shot["id"]
+ dur = shot["dur"]
+ print(f"\n镜{sid:02d} · {dur}秒 · 提交中...")
+ resp = submit(shot["p"], dur)
+ task_id = resp.get("id")
+ if not task_id:
+ print(f" ❌ 无任务ID")
+ results.append({"shot": sid, "ok": False})
+ continue
+ print(f" task: {task_id}")
+ dst = OUT / f"ep01-shot{sid:02d}.mp4"
+ ok, _ = poll_dl(task_id, dst)
+ if ok:
+ print(f" ✅ {dst.name} ({dst.stat().st_size//1024}KB)")
+ results.append({"shot": sid, "ok": True, "file": str(dst)})
+ else:
+ print(f" ❌ 失败")
+ results.append({"shot": sid, "ok": False})
+ time.sleep(5)
+
+print(f"\n{'='*40}")
+for r in results:
+ st = "✅" if r["ok"] else "❌"
+ print(f" {st} 镜{r['shot']:02d}")
+print(f"{'='*40}")