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}")