123 lines
7.7 KiB
Python
123 lines
7.7 KiB
Python
#!/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}")
|