数字我 Digital Me:全量入库(大文件走 Git LFS)
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s

This commit is contained in:
2026-10-10 16:24:43 +08:00
commit 0593ff7e62
1893 changed files with 361723 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
from __future__ import annotations
import argparse
import json
from pathlib import Path
BITRATES = {
(1, 1): [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
(1, 2): [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],
(1, 3): [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
(2, 1): [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
(2, 2): [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
(2, 3): [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
}
SAMPLE_RATES = {1: [44100, 48000, 32000], 2: [22050, 24000, 16000], 25: [11025, 12000, 8000]}
def synchsafe(b: bytes) -> int:
return (b[0] << 21) | (b[1] << 14) | (b[2] << 7) | b[3]
def analyze(path: Path) -> dict:
data = path.read_bytes()
pos = 0
if data[:3] == b"ID3" and len(data) >= 10:
pos = 10 + synchsafe(data[6:10])
if data[5] & 0x10:
pos += 10
frames = 0
duration = 0.0
bitrates = []
sample_rates = []
channel_modes = []
first_frame = None
while pos + 4 <= len(data):
h = int.from_bytes(data[pos : pos + 4], "big")
if (h >> 21) & 0x7FF != 0x7FF:
pos += 1
continue
version_bits = (h >> 19) & 0x3
layer_bits = (h >> 17) & 0x3
bitrate_idx = (h >> 12) & 0xF
sr_idx = (h >> 10) & 0x3
padding = (h >> 9) & 0x1
channel_mode = (h >> 6) & 0x3
if version_bits == 1 or layer_bits == 0 or bitrate_idx in (0, 15) or sr_idx == 3:
pos += 1
continue
version = {3: 1, 2: 2, 0: 25}[version_bits]
layer = {3: 1, 2: 2, 1: 3}[layer_bits]
br_version = 1 if version == 1 else 2
bitrate = BITRATES[(br_version, layer)][bitrate_idx] * 1000
sr = SAMPLE_RATES[version][sr_idx]
if layer == 1:
samples = 384
frame_len = ((12 * bitrate // sr) + padding) * 4
elif layer == 3 and version != 1:
samples = 576
frame_len = (72 * bitrate // sr) + padding
else:
samples = 1152
frame_len = (144 * bitrate // sr) + padding
if frame_len <= 4 or pos + frame_len > len(data):
pos += 1
continue
if first_frame is None:
first_frame = pos
frames += 1
duration += samples / sr
bitrates.append(bitrate)
sample_rates.append(sr)
channel_modes.append(channel_mode)
pos += frame_len
return {
"path": str(path),
"bytes": len(data),
"frames": frames,
"duration_seconds": round(duration, 6),
"duration_hms": f"{int(duration//60):02d}:{duration%60:05.2f}",
"sample_rate_hz": max(set(sample_rates), key=sample_rates.count) if sample_rates else None,
"channels": "mono" if channel_modes and all(x == 3 for x in channel_modes) else "stereo",
"average_bitrate_kbps": round(sum(bitrates) / len(bitrates) / 1000, 2) if bitrates else None,
"min_bitrate_kbps": min(bitrates) // 1000 if bitrates else None,
"max_bitrate_kbps": max(bitrates) // 1000 if bitrates else None,
"vbr": len(set(bitrates)) > 1,
"first_audio_frame_offset": first_frame,
}
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("path", type=Path)
args = parser.parse_args()
print(json.dumps(analyze(args.path), ensure_ascii=False, indent=2))
+712
View File
@@ -0,0 +1,712 @@
from __future__ import annotations
import re
from pathlib import Path
from docx import Document
from docx.enum.section import WD_SECTION
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK, WD_LINE_SPACING
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Inches, Pt, RGBColor
OUT = Path(r"D:\workspace\digital_me\artifacts\《我为你研墨》音频对齐脚本与分镜提示词.docx")
INK = "241C1A"
MUTED = "6E625D"
CINNABAR = "8B1E1E"
GOLD = "B88746"
PAPER = "F7F1E7"
PALE = "EFE9E1"
GRID = "D9D0C6"
WHITE = "FFFFFF"
BLACK = "000000"
FONT = "Microsoft YaHei"
SERIF = "SimSun"
STYLE_PREFIX = (
"唐代水墨×工笔重彩,2.5D半景深电影感构图;9:16竖屏,1080×1920;"
"主色调墨黑、暗红与暖金烛光,冷灰为辅;细腻工笔重彩结合氤氲水墨晕染,"
"朱砂点睛,胶片颗粒12%,长焦浅景深,24fps电影观感;柔光晕染,主体亮度-1EV;"
"烛火为唯一主光源,暖金色温3200K,光线从画面右上方落下,阴影投向左下并保留细节;"
"无现代元素,无文字,无水印;画面下沿15%预留字幕安全区;固定机位微动,镜头位移不超过20%。"
)
NEGATIVE = (
"现代服装、现代建筑、霓虹灯、塑料质感、欧美面孔、卡通Q版、赛博朋克、过度饱和、纯黑死阴影、"
"强烈镜头抖动、快速推拉、广角畸变、人物面部突变、手指畸形、多余肢体、服饰穿模、烛光方向漂移、"
"画面文字、字幕、水印、Logo、边框、横屏构图。"
)
SCENES = [
dict(n=1, tc="00:00.00–00:14.00", dur="14.00s", part="Intro", frame="图3|掖庭描红的孤女",
lyric="你杀了我祖父的那一年 / 我还不会写字",
beats=[("00:00.00–00:01.00", "黑场中研墨声先行,画面从黑缓慢显出。"),
("00:01.00–00:04.00", "俯视机位沿高墙缓慢下推,揭示伏案描红的孤女。"),
("00:04.00–00:10.00", "烛光渐亮;女孩笨拙落笔,高墙与长影压住人物。"),
("00:10.00–00:14.00", "推镜放缓并停住,停留在红簪与未写稳的笔画。")],
subtitles=[("00:02.00–00:08.00", "你杀了我祖父的那一年"), ("00:08.00–00:14.00", "我还不会写字")],
prompt="开场闪回。俯视机位从高耸宫墙顶端缓慢下推约20%,暗蓝粗布囚衣的幼年上官婉儿背对镜头,伏在窄矮木桌前执笔描红;发髻别一支朱砂红簪花,作为画面唯一鲜明暖色。两侧暗红宫墙形成强透视并占画面70%以上,压向中心;墙根孤烛呼吸般摇曳,阴影随火焰轻缓流动。前1秒黑场,前3秒以研墨声为主,第4秒烛光渐亮。女孩落笔笨拙而克制,最后4秒镜头几乎静止,突出“尚不会写字”的脆弱与压迫。",
transition="开场黑场1.0s淡入;镜尾以0.8s叠化进入镜2。", split="建议生成两段7s:A段完成俯视下推与人物揭示;B段保持同构图,仅保留落笔、烛火和影子微动。"),
dict(n=2, tc="00:14.00–00:30.00", dur="16.00s", part="Verse 1", frame="图13|将尽烛火·人小墙大",
lyric="掖庭里长大 / 罪臣的孙女",
beats=[("00:14.00–00:18.00", "极缓推进,门洞形高墙逐渐吞没画面边缘。"),
("00:18.00–00:25.00", "镜头停住;人物剪影与将尽红烛形成极端体量反差。"),
("00:25.00–00:30.00", "烛焰变弱、烛泪流下,冷灰空间继续压迫人物。")],
subtitles=[("00:15.00–00:22.00", "掖庭里长大"), ("00:22.00–00:30.00", "罪臣的孙女")],
prompt="极缓推进约2秒后近乎静止。高耸暗红宫墙围出“门”字形门洞,门洞正中偏下是一支将尽红烛,烛焰仅指甲盖大小;烛旁素衣女子剪影占画面不到8%,人与墙形成极端体量对比。烛泪沿烛身缓缓滑下,在烛台底部凝成暗红蜡堆。整体冷灰偏重,仅烛火周围10%范围保留暖金光晕;让烛焰、烛泪与衣摆承担全部动态,人物始终克制不动。",
transition="0.8–1.0s叠化,烛火光斑溶入镜3砚面反光。", split="建议8s+8s同机位续写;第二段用首尾帧延续功能,避免人物比例、门洞结构和烛台位置跳变。"),
dict(n=3, tc="00:30.00–00:44.00", dur="14.00s", part="Verse 1", frame="图1|一滴水入砚",
lyric="我把恨磨成了墨 / 一滴,不许它响",
beats=[("00:30.00–00:33.00", "微距俯拍砚池,水滴在画面上方悬停。"),
("00:33.00–00:36.00", "水滴落入墨心,三圈涟漪与一次极轻画面震颤同步。"),
("00:36.00–00:41.00", "墨气升起至画面三分之一,烛斑在黑镜中虚化。"),
("00:41.00–00:44.00", "墨气散尽,砚面重归寂静。")],
subtitles=[("00:32.00–00:37.00", "我把恨磨成了墨"), ("00:37.00–00:44.00", "一滴,不许它响")],
prompt="微距俯拍,砚台占画面90%,背景纯黑。水滴从画面上方悬停约2秒后落入墨池正中央;入墨瞬间,以落点为圆心扩散三圈细密涟漪,并产生一次极轻微画面震颤。水雾凝成一缕墨气柱,上升至画面约三分之一后缓慢摇曳消散。墨面如黑镜,映出暖金色焦外烛火光斑;全过程强调静、慢、压抑,水滴的“响”只通过视觉震颤暗示。",
transition="1.0s叠化;墨池黑色自然溶入镜4左半脸阴影。", split="建议7s+7s:第一段完成悬滴与落墨;第二段以涟漪首帧衔接,完成墨气升散。"),
dict(n=4, tc="00:44.00–00:58.00", dur="14.00s", part="Verse 1", frame="图4|研墨·墨映半脸",
lyric="如今我替你,把诏书写得漂亮 / 一笔一划,都是你要的锋芒",
beats=[("00:44.00–00:49.00", "中景缓推,女子持续画圆研墨。"),
("00:49.00–00:54.00", "现实半脸与砚中倒影组成对称,墨纹轻晃。"),
("00:54.00–00:58.00", "她停手凝视倒影,睫毛微动,镜头不再前进。")],
subtitles=[("00:45.00–00:51.00", "如今我替你,把诏书写得漂亮"), ("00:51.00–00:58.00", "一笔一划,都是你要的锋芒")],
prompt="中景缓推约20%。成年上官婉儿低头研墨,发髻插朱砂红簪;右后方暖金烛光照亮右半张脸,左半脸隐入墨色阴影。她手持墨锭缓慢而均匀地画圆,砚中墨面如黑镜,映出模糊半脸,与现实被照亮的半脸形成左右对称——一半真实、一半倒影。墨面倒影随研磨纹理轻晃。第10秒停手,凝视砚中自己,睫毛微动,最后4秒保持压抑静止。",
transition="镜尾0.5s压暗,再以0.8s叠化进入祖父面容。", split="7s+7s。A段持续研墨与缓推;B段从同角度继续,动作由研墨减速过渡到停手凝视。"),
dict(n=5, tc="00:58.00–01:10.00", dur="12.00s", part="Pre-Chorus 1", frame="图12|砚镜浮祖父面(初现)",
lyric="该恨你 / 还是该谢你",
beats=[("00:58.00–01:02.00", "中近景缓推;墨面涟漪出现。"),
("01:02.00–01:06.00", "白发祖父的写意面容在砚中浮现,短暂停留。"),
("01:06.00–01:10.00", "面容随墨纹消散,女子瞳孔保留最后一瞬缩影。")],
subtitles=[("01:00.00–01:05.00", "该恨你"), ("01:05.00–01:10.00", "还是该谢你")],
prompt="中近景缓推约15%。女子俯身凝视砚中墨面,墨面如镜;涟漪从中心荡开后,浮现一位清瘦威严的白发老者写意面容——五官虚化但可辨认为祖父上官仪。面容停留约3秒后随墨纹散成黑色烟雾。右侧烛火轻跳,暖金光晕落在女子侧脸;女子瞳孔中短暂映出祖父面容的微缩倒影。此处情绪是“心结初现”,不可做成惊悚鬼影。",
transition="1.0s叠化;墨烟向画面深处延伸为宫廊暗部。", split="可直接生成12s;若平台上限10s,先生成8s主动作,再以4s首尾帧延长完成消散。"),
dict(n=6, tc="01:10.00–01:26.00", dur="16.00s", part="Verse 2", frame="图10|搀老帝王(皆背影)",
lyric="你老了,手也抖了 / 我扶你上朝",
beats=[("01:10.00–01:16.00", "跟随长镜缓推,两人背影进入宫廊纵深。"),
("01:16.00–01:22.00", "帝王袖口微颤、步伐迟缓;女子步伐稳定。"),
("01:22.00–01:26.00", "抵达朝堂暖光前,帝王加重搭臂力道。")],
subtitles=[("01:11.00–01:18.00", "你老了,手也抖了"), ("01:18.00–01:26.00", "我扶你上朝")],
prompt="幽深唐代宫廊纵深透视,跟随长镜缓慢前推。画面左侧为立髻玄衣的上官婉儿背影,身形挺拔;右侧为身着明黄龙袍的晚年武则天背影,身形微佝偻。婉儿搀扶帝王缓步前行,帝王袖口细微颤动。两侧红柱与灯火缓慢后退,前方朝堂入口暖金光晕渐亮;两人长影投在青砖地面,随烛火轻摆。女子步伐稳,帝王步伐迟缓。第14秒帝王手在女子手臂上加重力道,二人停在光晕边缘。全程不露正脸。",
transition="0.5s压暗硬切到掌心特写,让空间尺度骤然收紧。", split="8s+8s同方向运动。第二段必须沿用第一段末帧,保持人物左右位置、衣色、步幅与宫廊透视一致。"),
dict(n=7, tc="01:26.00–01:36.00", dur="10.00s", part="Chorus 1|意象过门", frame="图9|掌上起墨风",
lyric="(掌上起风 / 婉儿落笔)",
beats=[("01:26.00–01:29.00", "女子手掌大特写,掌心墨气聚拢。"),
("01:29.00–01:34.00", "墨气螺旋三圈,速度由慢渐快。"),
("01:34.00–01:36.00", "墨风消散,右上浮出笔尖落纸虚影。")],
subtitles=[("01:27.00–01:31.50", "(掌上起风)"), ("01:31.50–01:36.00", "(婉儿落笔)")],
prompt="前景大特写一只摊开的女子手掌,占画面约60%。掌心生出黑色墨气漩涡,墨气螺旋上升如风,旋转三圈,速度由慢渐快后消散。另一只手执毛笔,在画面左侧虚化区域蘸墨;右下烛光斜照手指边缘,勾勒暖金边光。墨气消散瞬间,画面右上方隐约浮出笔尖落纸的虚影,完成从“掌上风”到“落笔”的意象连接。手指数量、结构和腕部饰物保持自然稳定。",
transition="0.5s快速叠化并卡节拍进入棋盘俯拍。", split="单段10s生成;优先选择手部稳定版本,必要时牺牲墨气复杂度以避免手指畸变。"),
dict(n=8, tc="01:36.00–01:48.00", dur="12.00s", part="Chorus 1", frame="图8|黑白棋·两双手",
lyric="这盘棋里 / 谁是谁的因果",
beats=[("01:36.00–01:42.00", "俯拍微推,两手各持一子悬于棋盘。"),
("01:42.00–01:45.00", "帝王手落黑子,烛火一跳,落点泛墨晕。"),
("01:45.00–01:48.00", "女子手落白子,白梅轻颤,形成回应。")],
subtitles=[("01:39.00–01:43.50", "这盘棋里"), ("01:43.50–01:48.00", "谁是谁的因果")],
prompt="俯拍青玉围棋盘并极缓推近约10%,棋盘占画面70%。左侧青年女子纤白手腕戴银镯、执白子;右侧老年帝王枯瘦手戴玉扳指、执黑子。棋盘已有零星黑白棋子,暗示对弈已久。第6秒老者手落黑子,烛火同步跳动,落点泛起一圈水墨晕染光纹;第9秒女子手悬停半拍后落下白子,出现同样但更淡的晕染。右下角斜插白梅一枝,随烛光轻颤。两手不得交叉或变形。",
transition="0.3s硬切进入镜9,制造“棋局落子—家宅焚毁”的因果冲击。", split="可8s+4s;A段完成对峙与黑子,B段从黑子落定的静帧延续并完成白子。"),
dict(n=9, tc="01:48.00–02:00.00", dur="12.00s", part="Pre-Chorus 2", frame="图5|焚宅·递笔(后期分屏)",
lyric="该恨你,还是该谢你 / 你毁了我的家,又给了我笔",
beats=[("01:48.00–01:54.00", "左屏焚宅、女子剪影不动;右屏手伸向递来的笔。"),
("01:54.00–01:58.00", "中央墨痕下滴,右屏指尖触笔时烛泪落下。"),
("01:58.00–02:00.00", "火焰爆裂与研墨声同步,两屏同时轻震。")],
subtitles=[("01:49.00–01:54.00", "该恨你,还是该谢你"), ("01:54.00–02:00.00", "你毁了我的家,又给了我笔")],
prompt="后期制作左右对称分屏。左屏:燃烧的唐代宅邸,火焰由下向上舔舐屋檐,飞灰与火星掠过镜头;一名女子剪影立在火前纹丝不动,火光映红半面墙。右屏:烛光下一只年轻女子手缓慢接住画外递来的毛笔,指尖触到笔杆瞬间,一滴烛泪沿烛身滑落。两屏中央以一道竖直墨痕分隔,约第4秒开始向下滴墨,底部晕开。第10秒两屏同时极轻震动,形成火焰爆裂与研墨声的情绪同拍。",
transition="镜8至镜9使用0.3s硬切;镜9末端再以0.8s墨色叠化进入砚面。", split="不要让图生视频模型直接生成分屏。分别生成“焚宅”和“递笔”两条12s素材,在剪辑软件中50/50合成并叠加中央墨痕。"),
dict(n=10, tc="02:00.00–02:16.00", dur="16.00s", part="Chorus 1|视角升格", frame="图6|砚面墨镜化江山",
lyric="我为你研墨,也为自己活 / 仇人的江山,我替她说",
beats=[("02:00.00–02:04.00", "俯拍砚面,近景烛火映在墨中。"),
("02:04.00–02:11.00", "镜头升起后拉,砚面变形为水墨宫城与万家灯火。"),
("02:11.00–02:16.00", "全景定住;女子像棋子般立在最高城楼。")],
subtitles=[("02:03.00–02:08.00", "我为你研墨,也为自己活"), ("02:08.00–02:16.00", "仇人的江山,我替她说")],
prompt="镜头从俯拍砚面开始,前4秒砚中墨面如黑镜映出近景烛火;随后缓慢升起并后拉,墨面自然“化”为水墨宫殿群的夜间俯瞰视角。万家灯火如棋盘星罗,远处宫阙在云雾中隐现。上官婉儿背影立于最高城楼,位于画面上三分之一,剪影仅占画面约5%。镜头升起过程中,近景烛火连续过渡为万家灯火星点;第11秒到达最远景并定格至镜尾,女子独立城楼,既像执笔者也像棋子。",
transition="1.2s长叠化进入朱笔微距,宫城灯火收束为朱砂笔尖高光。", split="8s+8s。A段完成砚面与变形起势;B段从宫城初现的末帧继续后拉,完成远景定格。尽量使用首尾帧/运镜延长功能。"),
dict(n=11, tc="02:16.00–02:30.00", dur="14.00s", part="Verse 2", frame="图2|朱笔落一道锋",
lyric="你不问我恨不恨,只看我写得好不好 / 我把心藏进批注的小字号",
beats=[("02:16.00–02:22.00", "笔尖悬停纸面,银镯反光,犹豫被拉长。"),
("02:22.00–02:26.00", "朱笔斜落一道锋,红墨微粒迸溅并晕染。"),
("02:26.00–02:30.00", "笔尖在末端停顿、轻颤,随后抬离纸面。")],
subtitles=[("02:17.00–02:23.00", "你不问我恨不恨,只看我写得好不好"), ("02:23.00–02:30.00", "我把心藏进批注的小字号")],
prompt="微距特写成年女子执笔右手。蘸饱朱砂红墨的笔尖悬停宣纸上方,右上方烛光暖铺纸面,腕部银镯反射高光。前6秒保持克制悬停,只允许指尖与笔毫轻微颤动;第6秒至第9秒,笔尖从右上向左下落下一道锋利红色斜划,2至3颗红墨微粒从笔锋处迸溅,划痕边缘在宣纸上晕开暗红。落笔后笔尖在末端停顿约2秒,微颤后抬离纸面。确保毛笔结构、手指和朱砂轨迹连续。",
transition="0.8–1.0s叠化;朱砂划痕压暗为半开宫门的缝隙。", split="7s+7s。A段专注悬停与犹豫;B段以同一构图完成落笔、停顿和抬笔。"),
dict(n=12, tc="02:30.00–02:44.00", dur="14.00s", part="Chorus 2|人物谜面", frame="基准图|人物定调",
lyric="你看不透我 / 我也看不透我",
beats=[("02:30.00–02:36.00", "静止构图缓推,背影望向半开宫门。"),
("02:36.00–02:39.00", "烛火突然跳动,女子肩头轻颤。"),
("02:39.00–02:44.00", "人物恢复平静,面部始终不可见。")],
subtitles=[("02:32.00–02:38.00", "你看不透我"), ("02:38.00–02:44.00", "我也看不透我")],
prompt="静止构图中极缓推近约10%。上官婉儿背影立于孤烛旁,发髻别朱砂红簪,长裙拖地;她背对镜头望向半开的宫门,面部全程不可见。右上方暖金烛光勾亮发丝与衣褶边缘,窗外仅见夜色宫阙剪影;烛泪缓慢滑落。第7秒烛火突然跳动一次,女子肩头极轻一颤,随即恢复平静。暗调中保留衣褶与宫门细节,以“始终不见脸”强化彼此与自我的不可看透。",
transition="0.5s黑场后进入镜13同一宫廊,使谜面转入秘密。", split="7s+7s同构图;第二段只做烛跳、肩颤和恢复,禁止改变人物站位与宫门开合角度。"),
dict(n=13, tc="02:44.00–02:56.00", dur="12.00s", part="Verse 2|秘密浮现", frame="图11|身后血诏虚影",
lyric="这一路,我们谁也没提过 / 那道诏",
beats=[("02:44.00–02:48.00", "跟随两人背影缓推,地面开始泛红。"),
("02:48.00–02:53.00", "血红诏书虚影展开,两人脚步踏过。"),
("02:53.00–02:56.00", "诏影散为红墨气;女子顿半拍后继续。")],
subtitles=[("02:46.00–02:52.00", "这一路,我们谁也没提过"), ("02:52.00–02:56.00", "那道诏")],
prompt="与镜6相同的幽深宫廊,但时间更晚、帝王背影更佝偻。跟随长镜缓慢前推,女子与帝王并行前行。青砖地面从二人脚下浮现血红诏书虚影:卷轴缓缓展开,朱砂字迹若隐若现但不可辨读具体内容;脚步踏过诏书虚影。诏影停留约3秒后散为红色墨气,被脚步带起的微风卷走。第10秒女子脚步停顿半拍,似有察觉,随后继续向前。红色只存在于虚影与朱砂,不可污染全片色调。",
transition="1.2s叠化;红墨气聚拢成镜14砚面涟漪。", split="可8s+4s,或用10s主素材加2s速度拉伸。续段必须保持两人步向、背影比例和宫廊灯位。"),
dict(n=14, tc="02:56.00–03:12.00", dur="16.00s", part="Bridge|情感高潮", frame="图12|砚镜浮祖父面(再现)",
lyric="祖父啊,我没替你报仇 / 我活成了比报仇更长的一句咒 / 或者,只是一个想活下去的人",
beats=[("02:56.00–03:00.00", "缓推砚面,祖父面容由墨纹中逐渐清晰。"),
("03:00.00–03:05.00", "祖父停留,女子睫毛微动、嘴唇轻颤。"),
("03:05.00–03:08.00", "烛火加剧,烛泪落砚,祖父面容随涟漪化散。"),
("03:08.00–03:12.00", "墨面映出女子自己,与祖父短暂重叠;她缓缓直身,画面转暗。")],
subtitles=[("02:57.00–03:02.00", "祖父啊,我没替你报仇"), ("03:02.00–03:07.00", "我活成了比报仇更长的一句咒"), ("03:07.00–03:12.00", "或者,只是一个想活下去的人(30pt,#CCCCCC)")],
prompt="中近景缓推约20%,沿用镜5构图但情绪更浓。女子俯身凝视砚中墨面,涟漪荡开后祖父上官仪的白发面容再次浮现;这次更清晰、更温和也更威严,停留约5秒。女子睫毛微动、嘴唇轻颤但不出声。第9秒起烛火摇曳幅度加大,第10秒一滴烛泪落入砚池,涟漪带走祖父面容。面容消散后,墨面映出女子自己的脸,并与祖父轮廓短暂重叠一帧。最后4秒女子缓缓直起身,烛光渐弱,画面渐暗。情绪应是克制后的崩裂与自我承认,不流泪、不嚎哭、不做夸张表演。",
transition="镜尾1.0s压黑,接镜15诏书暖黄底;不额外延长总时长。", split="强烈建议8s+8s并生成多版。A段完成祖父清晰出现与人物反应;B段从稳定面容末帧延续,完成烛泪、消散、重叠与直身。"),
dict(n=15, tc="03:12.00–03:19.15", dur="7.15s", part="Outro", frame="图7|墨字生宫阙",
lyric="墨干了,诏成了 / 风还在掌上",
beats=[("03:12.00–03:14.00", "俯拍明黄诏书,墨字晕开。"),
("03:14.00–03:17.00", "笔画离纸化成微缩宫阙,镜头随之升起后拉。"),
("03:17.00–03:19.15", "宫城灯火逐盏熄灭,只剩城楼剪影与一盏灯,尾音归黑。")],
subtitles=[("03:13.00–03:17.00", "墨干了,诏成了"), ("03:17.00–03:19.15", "风还在掌上")],
prompt="从俯拍明黄诏书开始,前2秒墨字在纸上如水墨缓缓晕开;随后笔画脱离纸面,化为微缩宫阙楼阁并以2.5D方式升起。镜头随墨字上升并后拉,俯瞰夜色大明宫灯火如棋盘,与镜10形成闭环;上官婉儿背影独立最高城楼。第5秒到达远景并定格,最后2.15秒极缓变暗:万家灯火渐次熄灭,仅余城楼剪影与画面正中偏下的一盏灯,最后一盏灯随研墨声尾音熄灭,全黑结束。",
transition="03:19.15全黑结束;保留最后0.15s音频尾音,不再追加片尾卡。", split="单段7.15s生成。生成时不加入文字;字幕与全黑尾帧均在剪辑软件中完成。"),
]
def set_run_font(run, size=None, bold=None, color=None, name=FONT, italic=None):
run.font.name = name
run._element.get_or_add_rPr().rFonts.set(qn("w:ascii"), name)
run._element.get_or_add_rPr().rFonts.set(qn("w:hAnsi"), name)
run._element.get_or_add_rPr().rFonts.set(qn("w:eastAsia"), name)
if size is not None:
run.font.size = Pt(size)
if bold is not None:
run.bold = bold
if italic is not None:
run.italic = italic
if color is not None:
run.font.color.rgb = RGBColor.from_string(color)
def shade_paragraph(paragraph, fill, border=None):
ppr = paragraph._p.get_or_add_pPr()
shd = ppr.find(qn("w:shd"))
if shd is None:
shd = OxmlElement("w:shd")
ppr.append(shd)
shd.set(qn("w:fill"), fill)
if border:
pbdr = OxmlElement("w:pBdr")
left = OxmlElement("w:left")
left.set(qn("w:val"), "single")
left.set(qn("w:sz"), "18")
left.set(qn("w:space"), "8")
left.set(qn("w:color"), border)
pbdr.append(left)
ppr.append(pbdr)
def set_cell_shading(cell, fill):
tcpr = cell._tc.get_or_add_tcPr()
shd = tcpr.find(qn("w:shd"))
if shd is None:
shd = OxmlElement("w:shd")
tcpr.append(shd)
shd.set(qn("w:fill"), fill)
def set_cell_margins(cell, top=80, start=120, bottom=80, end=120):
tcpr = cell._tc.get_or_add_tcPr()
mar = tcpr.first_child_found_in("w:tcMar")
if mar is None:
mar = OxmlElement("w:tcMar")
tcpr.append(mar)
for tag, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
node = mar.find(qn(f"w:{tag}"))
if node is None:
node = OxmlElement(f"w:{tag}")
mar.append(node)
node.set(qn("w:w"), str(value))
node.set(qn("w:type"), "dxa")
def set_table_geometry(table, widths, indent=120):
table.autofit = False
table.alignment = WD_TABLE_ALIGNMENT.LEFT
tblpr = table._tbl.tblPr
tblw = tblpr.find(qn("w:tblW"))
if tblw is None:
tblw = OxmlElement("w:tblW")
tblpr.append(tblw)
tblw.set(qn("w:w"), str(sum(widths)))
tblw.set(qn("w:type"), "dxa")
tblind = tblpr.find(qn("w:tblInd"))
if tblind is None:
tblind = OxmlElement("w:tblInd")
tblpr.append(tblind)
tblind.set(qn("w:w"), str(indent))
tblind.set(qn("w:type"), "dxa")
grid = table._tbl.tblGrid
for child in list(grid):
grid.remove(child)
for width in widths:
col = OxmlElement("w:gridCol")
col.set(qn("w:w"), str(width))
grid.append(col)
for row in table.rows:
row.height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST
for idx, cell in enumerate(row.cells):
tcw = cell._tc.get_or_add_tcPr().find(qn("w:tcW"))
if tcw is None:
tcw = OxmlElement("w:tcW")
cell._tc.get_or_add_tcPr().append(tcw)
tcw.set(qn("w:w"), str(widths[idx]))
tcw.set(qn("w:type"), "dxa")
cell.width = Inches(widths[idx] / 1440)
cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
set_cell_margins(cell)
def set_repeat_table_header(row):
trpr = row._tr.get_or_add_trPr()
tbl_header = OxmlElement("w:tblHeader")
tbl_header.set(qn("w:val"), "true")
trpr.append(tbl_header)
def add_page_field(paragraph):
paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
run = paragraph.add_run("第 ")
set_run_font(run, size=8.5, color=MUTED)
fld_begin = OxmlElement("w:fldChar")
fld_begin.set(qn("w:fldCharType"), "begin")
instr = OxmlElement("w:instrText")
instr.set(qn("xml:space"), "preserve")
instr.text = " PAGE "
fld_end = OxmlElement("w:fldChar")
fld_end.set(qn("w:fldCharType"), "end")
run._r.extend([fld_begin, instr, fld_end])
tail = paragraph.add_run(" 页")
set_run_font(tail, size=8.5, color=MUTED)
def add_label_para(doc, label, text, after=5, shade=None):
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(0)
p.paragraph_format.space_after = Pt(after)
p.paragraph_format.line_spacing = 1.25
if shade:
p.paragraph_format.left_indent = Inches(0.12)
p.paragraph_format.right_indent = Inches(0.10)
p.paragraph_format.space_before = Pt(5)
p.paragraph_format.space_after = Pt(7)
shade_paragraph(p, shade, CINNABAR)
r = p.add_run(label)
set_run_font(r, size=10.2, bold=True, color=CINNABAR)
r2 = p.add_run(text)
set_run_font(r2, size=10.2, color=INK)
return p
def add_heading(doc, text, level):
p = doc.add_paragraph(style=f"Heading {level}")
p.paragraph_format.keep_with_next = True
r = p.add_run(text)
return p
def add_prompt(doc, prompt):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.16)
p.paragraph_format.right_indent = Inches(0.12)
p.paragraph_format.space_before = Pt(3)
p.paragraph_format.space_after = Pt(9)
p.paragraph_format.line_spacing = 1.22
shade_paragraph(p, PAPER, GOLD)
r = p.add_run(prompt)
set_run_font(r, size=9.4, color=INK)
return p
def parse_timecode(value):
minutes, seconds = value.split(":")
return int(minutes) * 60 + float(seconds)
def format_seconds(value):
text = f"{value:.2f}"
return text.rstrip("0").rstrip(".")
def scene_duration(s):
return float(s["dur"].removesuffix("s"))
def clean_original_prompt_timing(text):
# The reference prompts mixed 12–16 second cues with generator timing.
# Remove those cue numbers before adding a single authoritative 10-second schedule.
text = re.sub(r"第\d+(?:\.\d+)?秒(?:至第\d+(?:\.\d+)?秒)?", "随后", text)
text = re.sub(r"前\d+(?:\.\d+)?秒", "开头阶段", text)
text = re.sub(r"最后\d+(?:\.\d+)?秒", "结尾阶段", text)
text = re.sub(r"停留约\d+(?:\.\d+)?秒", "短暂停留", text)
text = re.sub(r"约\d+(?:\.\d+)?秒", "短暂", text)
text = re.sub(r"\d+(?:\.\d+)?秒", "短暂", text)
return text.replace("约短暂", "短暂").replace("随后至随后", "随后")
def make_10s_prompt(s):
scene_start = parse_timecode(s["tc"].split("–")[0])
duration = scene_duration(s)
scale = 10.0 / duration
schedule = []
for beat_range, action in s["beats"]:
beat_start_text, beat_end_text = beat_range.split("–")
beat_start = max(0.0, parse_timecode(beat_start_text) - scene_start)
beat_end = min(duration, parse_timecode(beat_end_text) - scene_start)
scaled_start = beat_start * scale
scaled_end = beat_end * scale
schedule.append(
f"第{format_seconds(scaled_start)}–{format_seconds(scaled_end)}秒:{action}"
)
visual = clean_original_prompt_timing(s["prompt"])
return (
STYLE_PREFIX
+ "生成时长严格10秒,首尾均为可剪辑的稳定画面,不生成字幕。"
+ "画面与动作总设定:"
+ visual
+ "以下10秒动作节拍为唯一时间指令:"
+ ";".join(schedule)
+ "。所有关键动作必须在第10秒前完成,动作连续、速度平缓,不突然跳帧。"
)
def speed_mapping(s):
duration = scene_duration(s)
speed = 10.0 / duration * 100.0
if abs(duration - 10.0) < 0.001:
return "10秒生成素材以100%速度完整使用,直接占满本镜10秒。"
if duration > 10:
return (
f"本镜成片目标{format_seconds(duration)}秒:将10秒生成素材设为{speed:.2f}%速度,"
f"等比拉伸至{format_seconds(duration)}秒;开启光流或帧混合,字幕仍按绝对时间码放置。"
)
return (
f"本镜成片目标{format_seconds(duration)}秒:将10秒生成素材设为{speed:.2f}%速度,"
f"等比压缩至{format_seconds(duration)}秒;镜15须保留最终全黑与0.15秒音频尾音。"
)
def configure_styles(doc):
styles = doc.styles
normal = styles["Normal"]
normal.font.name = FONT
normal._element.rPr.rFonts.set(qn("w:ascii"), FONT)
normal._element.rPr.rFonts.set(qn("w:hAnsi"), FONT)
normal._element.rPr.rFonts.set(qn("w:eastAsia"), FONT)
normal.font.size = Pt(10.5)
normal.font.color.rgb = RGBColor.from_string(INK)
normal.paragraph_format.space_before = Pt(0)
normal.paragraph_format.space_after = Pt(6)
normal.paragraph_format.line_spacing = 1.25
for level, size, before, after, color in [
(1, 16, 18, 10, CINNABAR),
(2, 13, 14, 7, INK),
(3, 11.5, 10, 5, GOLD),
]:
st = styles[f"Heading {level}"]
st.font.name = FONT
st._element.rPr.rFonts.set(qn("w:ascii"), FONT)
st._element.rPr.rFonts.set(qn("w:hAnsi"), FONT)
st._element.rPr.rFonts.set(qn("w:eastAsia"), FONT)
st.font.size = Pt(size)
st.font.bold = True
st.font.color.rgb = RGBColor.from_string(color)
st.paragraph_format.space_before = Pt(before)
st.paragraph_format.space_after = Pt(after)
st.paragraph_format.keep_with_next = True
for st_name in ("Header", "Footer"):
st = styles[st_name]
st.font.name = FONT
st._element.rPr.rFonts.set(qn("w:eastAsia"), FONT)
st.font.size = Pt(8.5)
st.font.color.rgb = RGBColor.from_string(MUTED)
def add_cover(doc):
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(62)
p.paragraph_format.space_after = Pt(16)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run("上官婉儿主题音乐视频")
set_run_font(r, size=11, bold=True, color=GOLD)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(8)
r = p.add_run("《我为你研墨》")
set_run_font(r, size=30, bold=True, color=INK, name=SERIF)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(28)
r = p.add_run("音频对齐脚本与分镜头提示词")
set_run_font(r, size=17, color=CINNABAR)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(36)
r = p.add_run("15 镜 · 15 条固定 10 秒提示词 · 199.152 秒音频成片")
set_run_font(r, size=10.5, color=MUTED)
table = doc.add_table(rows=6, cols=2)
set_table_geometry(table, [1900, 7460], indent=120)
rows = [
("音频", "我为你研墨(1).mp3"),
("实测", "03:19.15|48 kHz|立体声|约 184 kbps VBR"),
("叙事", "不会写字的孤女 → 替敌研墨写诏的女官 → 活成比报仇更长的一句咒"),
("输出", "1080×1920|H.264|30fps|8–12 Mbps"),
("版本", "音频对齐执行版|2026-08-11"),
]
set_repeat_table_header(table.rows[0])
for j, text in enumerate(("项目", "制作参数")):
set_cell_shading(table.cell(0, j), INK)
p = table.cell(0, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
r = p.add_run(text)
set_run_font(r, size=9.2, bold=True, color=WHITE)
for idx, (label, value) in enumerate(rows, start=1):
set_cell_shading(table.cell(idx, 0), PALE)
for j, text in enumerate((label, value)):
p = table.cell(idx, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
p.paragraph_format.line_spacing = 1.15
r = p.add_run(text)
set_run_font(r, size=9.6, bold=(j == 0), color=CINNABAR if j == 0 else INK)
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(28)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run("制作原则:每条图生视频提示词严格生成10秒,再通过等比变速映射到源音频时间轴;转场不额外延长成片。")
set_run_font(r, size=9.5, italic=True, color=MUTED)
doc.add_page_break()
def add_front_matter(doc):
add_heading(doc, "1. 执行摘要", 1)
add_label_para(doc, "交付定位|", "本稿把源音频、完整歌词版视觉构想与音频对齐版时间表合并为一份可执行的导演脚本。15个叙事镜头各对应1条固定10秒图生视频提示词,并给出10秒素材到绝对音频时间轴的变速映射。")
add_label_para(doc, "音频校验|", "源文件实测 199.152 秒(03:19.15),48 kHz、立体声、约184 kbps VBR。参考稿按199秒规划的镜15末端延长0.15秒,用于保留研墨声/混响尾音。")
add_label_para(doc, "时间规则|", "本稿所有区间采用“起点含、终点不含”的剪辑时间码;字幕入出点为执行起稿,最终应在剪映或 Premiere 的波形上按实际咬字前后微调 2–4 帧。转场叠化覆盖相邻素材,不额外增加总片长。")
add_label_para(doc, "生成规则|", "每条提示词的生成时长统一为10秒。提示词内的动作节拍已按原镜头时长等比压缩到0–10秒;生成后再按各镜“10秒素材映射”设置速度,即可恢复到199.152秒音频时间轴。")
add_heading(doc, "2. 统一视觉与声音基线", 1)
add_heading(doc, "2.1 完整统一风格前缀", 2)
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.16)
p.paragraph_format.right_indent = Inches(0.12)
p.paragraph_format.space_after = Pt(9)
p.paragraph_format.line_spacing = 1.22
shade_paragraph(p, PAPER, GOLD)
r = p.add_run(STYLE_PREFIX)
set_run_font(r, size=9.6, color=INK)
add_heading(doc, "2.2 全片负面提示词", 2)
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.16)
p.paragraph_format.right_indent = Inches(0.12)
p.paragraph_format.space_after = Pt(9)
shade_paragraph(p, "F4EEEE", CINNABAR)
r = p.add_run(NEGATIVE)
set_run_font(r, size=9.6, color=INK)
add_heading(doc, "2.3 字幕、音效与导出", 2)
table = doc.add_table(rows=7, cols=2)
set_table_geometry(table, [2100, 7260])
specs = [
("字幕字体", "思源宋体 Heavy / 方正粗宋;主歌词36pt,括号歌词28pt,Bridge第三句30pt"),
("字幕样式", "白色#FFFFFF,黑色描边1.5px;Bridge第三句淡灰#CCCCCC;淡入淡出0.3s"),
("字幕位置", "画面居中下方12%–15%安全区;不写入生成画面,统一后期添加"),
("环境音", "开场与结尾保留研墨声;落墨、落子、火焰爆裂、烛泪等按画面轻量点缀"),
("混音", "原曲全程不断;Bridge可将人声相对压低约2dB,突出古琴独白感"),
("导出", "1080×1920,H.264,30fps,8–12Mbps;成片精确至03:19.15"),
]
set_repeat_table_header(table.rows[0])
for j, text in enumerate(("项目", "制作规范")):
set_cell_shading(table.cell(0, j), INK)
p = table.cell(0, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
r = p.add_run(text)
set_run_font(r, size=9.2, bold=True, color=WHITE)
for idx, (label, value) in enumerate(specs, start=1):
set_cell_shading(table.cell(idx, 0), PALE)
for j, text in enumerate((label, value)):
p = table.cell(idx, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
r = p.add_run(text)
set_run_font(r, size=9.4, bold=(j == 0), color=CINNABAR if j == 0 else INK)
add_heading(doc, "3. 15镜音频时间轴与10秒生成规则速览", 1)
table = doc.add_table(rows=1, cols=5)
widths = [540, 1550, 760, 2730, 3780]
set_table_geometry(table, widths)
set_repeat_table_header(table.rows[0])
headers = ["镜", "时间码", "成片/生成", "画面", "对应歌词"]
for idx, text in enumerate(headers):
set_cell_shading(table.cell(0, idx), INK)
p = table.cell(0, idx).paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(0)
r = p.add_run(text)
set_run_font(r, size=8.8, bold=True, color=WHITE)
for s in SCENES:
cells = table.add_row().cells
values = [str(s["n"]), s["tc"].replace(".00", ""), s["dur"] + "\n10s", s["frame"], s["lyric"].replace(" / ", "\n")]
for idx, text in enumerate(values):
p = cells[idx].paragraphs[0]
p.paragraph_format.space_after = Pt(0)
p.paragraph_format.line_spacing = 1.05
p.alignment = WD_ALIGN_PARAGRAPH.CENTER if idx < 3 else WD_ALIGN_PARAGRAPH.LEFT
for line_i, line in enumerate(text.split("\n")):
if line_i:
p.add_run().add_break()
r = p.add_run(line)
set_run_font(r, size=8.4, color=INK, bold=(idx == 0))
if s["n"] % 2 == 0:
set_cell_shading(cells[idx], "FBF8F4")
set_table_geometry(table, widths)
add_label_para(doc, "总时长|", "生成素材共15条×10秒;剪辑后成片为199.152秒。镜1之前的1秒黑场包含在镜1内,镜15最后0.15秒保留源音频尾音。", after=8, shade=PAPER)
doc.add_page_break()
def add_scene(doc, s):
h = doc.add_paragraph(style="Heading 1")
h.paragraph_format.page_break_before = True
h.paragraph_format.keep_with_next = True
r = h.add_run(f"镜 {s['n']:02d}|{s['frame'].split('|')[-1]}")
set_run_font(r, size=16, bold=True, color=CINNABAR)
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(8)
p.paragraph_format.keep_with_next = True
shade_paragraph(p, PALE, GOLD)
items = [("时间码", s["tc"]), ("成片", s["dur"]), ("生成", "10.00s"), ("段落", s["part"]), ("参考图", s["frame"])]
for idx, (label, value) in enumerate(items):
if idx:
sep = p.add_run(" | ")
set_run_font(sep, size=8.8, color=GOLD)
lr = p.add_run(label + " ")
set_run_font(lr, size=8.8, bold=True, color=CINNABAR)
vr = p.add_run(value)
set_run_font(vr, size=8.8, color=INK)
add_label_para(doc, "歌词|", s["lyric"], after=9, shade=PAPER)
add_heading(doc, "导演脚本与镜内节拍", 2)
for timecode, action in s["beats"]:
add_label_para(doc, timecode + "|", action, after=4)
add_heading(doc, "字幕与剪辑", 2)
for timecode, line in s["subtitles"]:
add_label_para(doc, timecode + "|", line, after=4)
add_label_para(doc, "转场|", s["transition"], after=7)
add_heading(doc, "10秒图生视频提示词(可直接复制)", 2)
add_prompt(doc, make_10s_prompt(s))
add_heading(doc, "10秒素材映射到音频时间轴", 3)
add_label_para(doc, "变速|", speed_mapping(s), after=7)
if s["n"] == 9:
add_label_para(doc, "分屏|", "左侧焚宅与右侧递笔各生成一条10秒素材,二者使用同一10秒动作节拍;后期分别做相同变速,再以50/50画面合成。", after=7)
else:
add_label_para(doc, "衔接|", "优先使用10秒素材的完整动作弧线;变速后如需叠化,叠化区覆盖在本镜首尾稳定画面内,不改变主切点。", after=7)
add_label_para(doc, "统一负面提示|", NEGATIVE, after=7, shade="F4EEEE")
def add_workflow(doc):
h = doc.add_paragraph(style="Heading 1")
h.paragraph_format.page_break_before = True
r = h.add_run("4. 合成与验收清单")
set_run_font(r, size=16, bold=True, color=CINNABAR)
add_heading(doc, "4.1 合成顺序", 2)
steps = [
("01|固定10秒生成", "将15条“10秒图生视频提示词”分别送入模型,每条生成时长严格选择10秒;单镜至少保留2–3个备选。"),
("02|镜9分屏", "焚宅与递笔左右两侧各生成一条10秒素材,使用同一动作节拍;后期各占画面50%,中央叠加竖直墨痕。"),
("03|精确铺轨", "先锁定03:19.15音频,再按每镜“10秒素材映射”设置速度并放入绝对时间码;变速后镜尾必须与下一镜切点完全重合。"),
("04|字幕", "字幕独立成轨,不写入AI生成画面;按入出点初排后,以源音频咬字逐帧微调。"),
("05|转场", "叠化覆盖相邻素材,不额外占时;镜8到镜9使用0.3秒硬切,镜14到镜15先压黑再进入暖黄诏书。"),
("06|统一调色", "检查15镜烛光方向、肤色、暗红饱和度、墨黑层次与暖金色温;避免镜间亮度跳变。"),
("07|导出", "1080×1920、H.264、30fps、8–12Mbps;音频不得截断,尾帧在03:19.15归黑。"),
]
for label, text in steps:
add_label_para(doc, label + " ", text, after=6)
add_heading(doc, "4.2 成片验收", 2)
checks = [
("时长", "每条生成素材原始长度均为10.00秒;变速剪辑后总时长精确为03:19.15,转场未增加片长。"),
("人物", "上官婉儿发髻、朱砂簪、服饰与年龄阶段连续;武则天仅在指定镜头出现且均为背影/手部。"),
("光色", "主光始终从右上方落下;墨黑保留层次,暗红与朱砂不过饱和,暖金烛光约3200K。"),
("画面", "无现代元素、文字、水印、Logo、手指畸形、面部突变、穿模、色漂或异常镜头抖动。"),
("字幕", "下沿12%–15%安全区内清晰可读,不遮挡手、砚、棋子与人物关键表情;Bridge第三句样式更轻。"),
("叙事", "镜5与镜14同图不同情绪层级清楚;镜10与镜15形成江山意象闭环;镜9的恨/谢对照一眼可辨。"),
("声音", "原曲全程不断,环境音不过度抢人声,结尾研墨声与尾音自然归黑。"),
]
table = doc.add_table(rows=1, cols=2)
set_table_geometry(table, [1800, 7560])
set_repeat_table_header(table.rows[0])
for idx, text in enumerate(("验收项", "通过标准")):
set_cell_shading(table.cell(0, idx), INK)
p = table.cell(0, idx).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
r = p.add_run(text)
set_run_font(r, size=9.2, bold=True, color=WHITE)
for label, value in checks:
cells = table.add_row().cells
set_cell_shading(cells[0], PALE)
for idx, text in enumerate((label, value)):
p = cells[idx].paragraphs[0]
p.paragraph_format.space_after = Pt(0)
r = p.add_run(text)
set_run_font(r, size=9.2, bold=(idx == 0), color=CINNABAR if idx == 0 else INK)
set_table_geometry(table, [1800, 7560])
add_heading(doc, "4.3 参考与修订说明", 2)
add_label_para(doc, "参考文件|", "《我为你研墨》图生视频提示词(完整歌词版)与《我为你研墨》完整视频提示词(音频对齐版)。")
add_label_para(doc, "本版修订|", "独立复核音频长度为199.152秒;将15个镜头提示词全部统一为严格10秒,并把原成片节拍等比压缩进0–10秒;每镜新增变速百分比,使10秒素材能重新映射到原绝对时间轴。镜9左右两侧各生成10秒后再做分屏。")
add_label_para(doc, "使用提示|", "如最终歌词演唱咬字与本稿字幕入出点存在细微差异,以音频波形为准做2–4帧微调,但不要改变15镜主切点,除非同时复核全片叙事节奏。")
def build():
OUT.parent.mkdir(parents=True, exist_ok=True)
doc = Document()
sec = doc.sections[0]
sec.page_width = Inches(8.5)
sec.page_height = Inches(11)
sec.top_margin = Inches(1)
sec.bottom_margin = Inches(1)
sec.left_margin = Inches(1)
sec.right_margin = Inches(1)
sec.header_distance = Inches(0.492)
sec.footer_distance = Inches(0.492)
sec.different_first_page_header_footer = True
configure_styles(doc)
header = sec.header
hp = header.paragraphs[0]
hp.alignment = WD_ALIGN_PARAGRAPH.LEFT
r = hp.add_run("《我为你研墨》|音频对齐脚本与分镜提示词")
set_run_font(r, size=8.5, color=MUTED)
footer = sec.footer
add_page_field(footer.paragraphs[0])
core = doc.core_properties
core.title = "《我为你研墨》音频对齐脚本与分镜头提示词"
core.subject = "上官婉儿主题音乐视频制作执行稿"
core.author = ""
core.keywords = "我为你研墨, 上官婉儿, MV, 分镜, 图生视频, 音频对齐"
core.comments = "依据源音频与两份Markdown参考稿整理。"
add_cover(doc)
add_front_matter(doc)
for s in SCENES:
add_scene(doc, s)
add_workflow(doc)
doc.save(OUT)
print(OUT)
if __name__ == "__main__":
build()
+325
View File
@@ -0,0 +1,325 @@
from __future__ import annotations
from pathlib import Path
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Inches, Pt
import build_woyanmo_docx as b
OUT = Path(r"D:\workspace\digital_me\artifacts\《我为你研墨》完整歌词脚本与20镜分镜提示词(每镜10秒).docx")
SHOTS = [
dict(n=1, section="Intro", title="不会写字的孤女", lyric="(研墨声)你杀了我祖父的那一年 / 我还不会写字",
beats=[("0–2秒", "黑场中先出现研墨声,烛火从暗处显出。"), ("2–6秒", "俯视缓推,幼年婉儿在掖庭矮桌前歪斜描红。"), ("6–10秒", "镜头停在握笔的小手与高墙长影,红簪成为唯一亮色。")],
subtitles="1.0–6.0秒「你杀了我祖父的那一年」;6.0–9.8秒「我还不会写字」",
prompt="开场黑场约1秒,只有细微研墨声。烛火渐亮,俯视机位从高耸暗红宫墙缓慢下推,揭示幼年上官婉儿穿暗蓝粗布囚衣、背对镜头伏在窄矮木桌前描红。她握笔生涩,纸上笔画歪斜;发髻一支朱砂红簪为唯一鲜明色。宫墙占画面七成以上,长影压住弱小人物。最后四秒镜头近乎静止,仅烛焰、衣袖和笔尖微动。",
transition="0.8秒叠化,歪斜墨迹溶入成年婉儿笔下端正诏书。"),
dict(n=2, section="Verse 1", title="替她写漂亮的诏书", lyric="如今我替你 把诏书写得漂亮",
beats=[("0–3秒", "成年婉儿伏案,镜头从诏书卷首缓慢推向执笔手。"), ("3–7秒", "笔锋稳定行进,字迹端整,武则天身影在后景虚化。"), ("7–10秒", "婉儿停笔半拍,烛光将她的脸切成明暗两半。")],
subtitles="1.0–9.5秒「如今我替你 把诏书写得漂亮」",
prompt="成年上官婉儿身着玄红唐代女官服,夜间宫室内伏案书写明黄诏书。镜头从诏书卷首极缓推向执笔右手,毛笔写出端整有力的墨字;画面后景仅保留晚年武则天模糊侧影与明黄衣色,不抢主体。右上烛光照亮婉儿半张脸,另一半隐入墨影。第七秒后停笔半拍,目光仍落在诏书上,情绪克制、专业、不可揣测。",
transition="笔尖悬停处硬切至下一镜的锋利落笔。"),
dict(n=3, section="Verse 1", title="每一笔都是锋芒", lyric="一笔一划 都是你要的锋芒",
beats=[("0–3秒", "微距笔尖悬于纸面,朱砂与墨色并置。"), ("3–7秒", "笔锋快速但稳定地压、行、提,形成一道锐利转折。"), ("7–10秒", "镜头沿笔画末端滑动,墨迹边缘缓慢渗入宣纸。")],
subtitles="1.0–9.5秒「一笔一划 都是你要的锋芒」",
prompt="微距特写毛笔、手指与宣纸,成年婉儿的手腕佩细银镯。笔尖先悬停,随后以清晰的压笔、行笔、提锋完成一道锐利而端正的墨色笔画,笔锋末端带少量朱砂红高光,象征权力锋芒。镜头沿新写成的笔画缓慢横移,展示墨色在纸纤维中细微晕开;手指结构稳定,笔杆与笔毫不变形。",
transition="墨迹晕染扩满画面,叠化成掖庭冷灰墙面。"),
dict(n=4, section="Verse 1", title="掖庭长大的罪臣之孙", lyric="掖庭里长大 罪臣的孙女",
beats=[("0–3秒", "远景展示门字形高墙与极小人物。"), ("3–7秒", "少女提灯穿过狭长廊道,步伐安静。"), ("7–10秒", "她停在门洞中央,灯火变弱,墙体继续压迫。")],
subtitles="1.0–5.0秒「掖庭里长大」;5.0–9.5秒「罪臣的孙女」",
prompt="高耸暗红宫墙围出狭窄门字形通道,冷灰空气中,少年上官婉儿穿素色旧衣,手提一盏小灯缓慢穿过。人物只占画面约8%,巨大墙体形成强烈压迫。镜头固定,仅做极轻缓推;她走至门洞中央停下,灯焰渐弱,烛泪沿灯烛下滑。无人出现,无夸张表演,以空间体量表现“罪臣孙女”的身份烙印。",
transition="灯焰焦外光斑溶成砚池中的暖金倒影。"),
dict(n=5, section="Verse 1", title="把恨磨成墨", lyric="我把恨 磨成了墨 / 一滴 不许它响",
beats=[("0–3秒", "俯拍砚台,墨锭缓慢画圆,水滴在上方凝结。"), ("3–6秒", "水滴无声落入墨池,扩散三圈涟漪。"), ("6–10秒", "墨气升起又散去,砚面恢复黑镜般平静。")],
subtitles="0.8–5.0秒「我把恨 磨成了墨」;5.0–9.8秒「一滴 不许它响」",
prompt="微距俯拍黑色砚台占画面九成,女子手持墨锭缓慢画圆研磨。一滴清水在画面上方凝结,第三秒无声落入墨池中央,扩散三圈细密涟漪;落墨瞬间只有极轻视觉震颤,不出现剧烈水花。水雾化成细长墨气升至画面三分之一,再缓缓散去。砚面映着虚化暖金烛斑,最后恢复黑镜般平静。",
transition="黑色墨面中央裂开一道竖痕,进入火宅与递笔的对照画面。"),
dict(n=6, section="Pre 1", title="恨与谢的分屏", lyric="该恨你 还是该谢你 / 你毁了我的家 又给了我笔",
beats=[("0–3秒", "左侧祖宅燃烧,右侧一支毛笔悬在少女面前。"), ("3–7秒", "少女左手握紧、右手接笔,中央墨痕向下滴落。"), ("7–10秒", "火焰爆裂与接笔动作同拍,画面轻震后定住。")],
subtitles="0.5–4.5秒「该恨你 还是该谢你」;4.5–9.8秒「你毁了我的家 又给了我笔」",
prompt="左右对称分屏构图。左屏为唐代上官府宅在夜色中燃烧,火焰舔过屋檐,少女剪影站在火前不动;右屏为宫室烛光下一支毛笔从画外递到少女面前,她迟疑后伸手接住。中央分隔线是一道竖直墨痕,第三秒开始向下滴墨。第七秒火焰轻微爆裂与指尖握住笔杆同步,双屏极轻震动后稳定。建议左右画面分别生成10秒,再后期合成。",
transition="中央墨痕向两侧晕开,覆盖火焰并化作宫城夜色。"),
dict(n=7, section="Chorus 1", title="为她研墨,也为自己活", lyric="我为你研墨 也为自己活 / 仇人的江山 我替她说",
beats=[("0–3秒", "砚面与研墨手势近景,婉儿倒影浮现。"), ("3–7秒", "镜头升起后拉,砚面转化为水墨宫城。"), ("7–10秒", "婉儿立于城楼俯瞰灯火,身影虽小却居于制高点。")],
subtitles="0.5–4.8秒「我为你研墨 也为自己活」;4.8–9.8秒「仇人的江山 我替她说」",
prompt="从俯拍砚面和研墨手势开始,黑色墨面映出成年婉儿克制的半脸倒影。镜头缓慢升起并后拉,砚池边缘自然转化为水墨大明宫城墙与宫阙,近景烛火连续变成万家灯火。后段婉儿背影立于最高城楼,玄红衣摆被风轻动,人物虽小却位于画面上三分之一制高点,俯瞰仇人的江山,表达她在权力中争取生存。",
transition="宫城灯火排列成棋盘格,俯视进入黑白棋局。"),
dict(n=8, section="Chorus 1", title="看不透的棋局", lyric="你看不透我 我也看不透我 / 这盘棋里 谁是谁的 因果",
beats=[("0–4秒", "墨镜中婉儿的脸一半清晰、一半被墨纹遮住。"), ("4–8秒", "画面叠化为棋盘,两双手分别落黑白子。"), ("8–10秒", "棋子落点泛起墨晕,两只手同时退出画面。")],
subtitles="0.5–4.5秒「你看不透我 我也看不透我」;4.5–9.8秒「这盘棋里 谁是谁的 因果」",
prompt="前四秒中近景:成年婉儿凝视砚中自己,现实半脸清晰,倒影半脸被流动墨纹遮住,目光冷静而困惑。随后以墨纹叠化成青玉棋盘俯拍,左侧婉儿纤白手戴银镯落白子,右侧年老帝王枯瘦手戴玉扳指落黑子。两枚棋子先后落盘,落点各泛一圈淡墨晕;最后两只手同时缓慢退出,只留下未完棋局。",
transition="棋盘墨晕旋转上升,变成掌心的墨色风旋。"),
dict(n=9, section="Chorus 1|间奏", title="掌上起风,婉儿落笔", lyric="(掌上起风 婉儿落笔)",
beats=[("0–3秒", "女子摊开手掌,掌心墨气聚拢。"), ("3–7秒", "墨气旋转三圈上升,另一只手在焦外蘸墨。"), ("7–10秒", "墨风散开,毛笔从上方落向宣纸,停在触纸一刻。")],
subtitles="1.0–9.0秒「(掌上起风 婉儿落笔)」;使用28pt淡灰括号体",
prompt="女子摊开的手掌占画面六成,掌心黑色墨气聚拢并形成缓慢旋转的风涡,连续旋转三圈后上升。另一只手在左侧焦外区域执笔蘸墨,右下烛光勾亮指尖边缘。第七秒墨风向两侧散开,画面上方一支毛笔缓慢落向宣纸;第十秒停在笔毫刚触纸的瞬间。手指自然稳定,无多余手指和肢体。",
transition="笔毫触纸硬切到帝王凝视婉儿书写的宫室。"),
dict(n=10, section="Verse 2", title="她只看字写得好不好", lyric="你不问我恨不恨 你只看我写得好不好",
beats=[("0–3秒", "婉儿书写近景,帝王在后景审视。"), ("3–7秒", "镜头横移至诏书,展示整齐字列与朱砂批记。"), ("7–10秒", "帝王手指轻点纸面,婉儿没有抬头。")],
subtitles="0.8–9.7秒「你不问我恨不恨 你只看我写得好不好」",
prompt="宫室夜景,中近景拍成年婉儿伏案书写,晚年武则天在后景虚化端坐,只露明黄衣袖和审视的侧影。镜头缓慢横移到诏书,展示端整墨字与少量朱砂批记。第七秒帝王枯瘦手指从画外伸入,轻点纸面某一行;婉儿笔尖停顿半拍但不抬头,继续书写。情绪是无声考核,不做争吵或夸张对峙。",
transition="镜头沿帝王指尖下移,进入纸边极小批注的微距。"),
dict(n=11, section="Verse 2", title="把心藏进小字号", lyric="我也学会了 把心 藏进批注的小字号",
beats=[("0–3秒", "极小字号批注在宣纸边缘逐行出现。"), ("3–7秒", "笔尖写到一个字时微停,墨迹化成短暂心形涟漪。"), ("7–10秒", "婉儿袖口遮住批注,诏书正文保持端正。")],
subtitles="0.8–9.7秒「我也学会了 把心 藏进批注的小字号」",
prompt="超近景拍宣纸边缘与纤细笔尖,诏书正文大字端正,边缘批注字号极小、密而整齐,不要求文字可辨读。婉儿写到一处时笔尖微停,墨面反光短暂形成近似心形的细小涟漪,随即恢复普通墨迹。后段她轻移玄色衣袖遮住批注区域,只留下公开正文。避免真实可读文字,强调“隐藏”而非魔幻发光。",
transition="玄色袖口掠过画面,叠化成长廊中搀扶帝王的玄衣背影。"),
dict(n=12, section="Verse 2", title="扶老帝王上朝", lyric="你老了 手也抖了 我扶你上朝",
beats=[("0–3秒", "幽深宫廊中,两人背影缓慢前行。"), ("3–7秒", "帝王袖口颤动、步履迟缓,婉儿手臂稳定。"), ("7–10秒", "前方朝堂暖光增强,帝王加重搭臂力道。")],
subtitles="0.8–5.0秒「你老了 手也抖了」;5.0–9.8秒「我扶你上朝」",
prompt="幽深唐代宫廊纵深透视,固定跟随镜头缓慢前推。左侧玄衣上官婉儿背影挺拔,右侧身着明黄龙袍的晚年武则天略显佝偻,婉儿搀扶她向朝堂走去。帝王袖口和手指细微颤动,步伐迟缓;婉儿步伐稳定。两侧红柱与灯火缓慢后退,前方暖金朝堂光晕渐亮。第八秒帝王在婉儿手臂上加重力道,全程不露正脸。",
transition="二人长影拉长,地面影子逐渐变成一卷血红诏书。"),
dict(n=13, section="Verse 2", title="谁也没提过那道诏", lyric="这一路 我们谁也没提过 那道诏",
beats=[("0–3秒", "延续宫廊跟随,青砖地面开始浮出红色卷轴虚影。"), ("3–7秒", "诏书虚影展开,两人脚步从其上踏过。"), ("7–10秒", "诏影散为红墨气,婉儿停半拍又继续走。")],
subtitles="0.8–6.0秒「这一路 我们谁也没提过」;6.0–9.8秒「那道诏」",
prompt="延续上一镜同一宫廊、同一背影与光位。两人缓步前行时,青砖地面从脚下浮现一卷血红诏书虚影,卷轴缓慢展开,朱砂字迹模糊不可辨读。二人脚步踏过虚影,没有低头。第七秒诏书散成红色墨气,被步风卷走;婉儿脚步停顿半拍,随后继续搀扶帝王前行。红色只用于诏影,不污染整体暗调。",
transition="红墨气上升并遮满画面,化成第二次Pre的火光与笔影。"),
dict(n=14, section="Pre 2", title="第二次选择", lyric="该恨你 还是该谢你 / 你毁了我的家 又给了我笔",
beats=[("0–3秒", "婉儿眼中闪回焚宅与祖父剪影。"), ("3–7秒", "现实中她握紧毛笔,火光倒影与烛光重合。"), ("7–10秒", "她松开紧绷手指,重新落笔,完成主动选择。")],
subtitles="0.5–4.5秒「该恨你 还是该谢你」;4.5–9.8秒「你毁了我的家 又给了我笔」",
prompt="成年婉儿中近景,先凝视手中毛笔。她瞳孔中短暂闪回燃烧的祖宅与白发祖父剪影,火光倒影和现实烛光在眼中重叠。第三秒手指用力握紧笔杆,指节泛白;第七秒她缓慢放松手指,深吸气但不流泪、不哭喊,随后把笔落向诏书。与第一次Pre相比,这一镜不是被动接笔,而是她清醒地再次选择使用这支笔。",
transition="笔尖落下的一滴墨扩散,进入第二次更明亮、更主动的宫城全景。"),
dict(n=15, section="Chorus 2", title="第二次为自己活", lyric="我为你研墨 也为自己活 / 仇人的江山 我替她说",
beats=[("0–3秒", "婉儿研墨并抬眼,目光从砚面转向宫城。"), ("3–7秒", "墨迹化为朝堂奏章与宫阙,镜头升高。"), ("7–10秒", "她立于朝堂与宫城之间,主动向前一步。")],
subtitles="0.5–4.8秒「我为你研墨 也为自己活」;4.8–9.8秒「仇人的江山 我替她说」",
prompt="第二次副歌采用更坚定的视觉。成年婉儿研墨后缓慢抬眼,目光从砚面转向前方。黑色墨迹从砚边流出,连续化为摊开的奏章、朝堂台阶与远处宫阙,镜头缓慢升高。婉儿从暗处向暖金光区迈出一步,玄红女官服衣摆轻动;她不再只是城楼上的孤小棋子,而是站在文字与江山之间的执笔者。",
transition="朝堂地砖转化为棋盘,婉儿迈出的一步对应白子落盘。"),
dict(n=16, section="Chorus 2", title="第二盘棋的答案", lyric="你看不透我 我也看不透我 / 这盘棋里 谁是谁的 因果",
beats=[("0–3秒", "铜镜中出现婉儿、祖父与帝王三重淡影。"), ("3–7秒", "镜面裂成棋盘格,白子落在关键位置。"), ("7–10秒", "黑子周围墨色退开,棋局仍未结束。")],
subtitles="0.5–4.5秒「你看不透我 我也看不透我」;4.5–9.8秒「这盘棋里 谁是谁的 因果」",
prompt="铜镜与砚面融合的黑镜中,成年婉儿自己的脸最清晰,白发祖父和晚年帝王只以淡淡轮廓在两侧重叠,三者随墨纹轻微错位。第三秒镜面逐格转化为青玉棋盘,婉儿戴银镯的手把白子落在关键位置,周围黑子不消失但墨色阴影逐渐退开。最后棋局仍未终结,镜头停在白子与婉儿手指离开的空隙。",
transition="白子落点泛出的墨晕旋成更大的掌上风。"),
dict(n=17, section="Chorus 2|间奏", title="风从掌上吹向江山", lyric="(掌上起风 婉儿落笔)",
beats=[("0–3秒", "掌心墨风比第一次更强,卷起细小纸页。"), ("3–7秒", "墨风沿手臂流向笔尖,婉儿落笔。"), ("7–10秒", "笔画化作宫阙轮廓,风越过城楼吹向远方。")],
subtitles="1.0–9.0秒「(掌上起风 婉儿落笔)」;使用28pt淡灰括号体",
prompt="呼应镜9但规模升级。婉儿摊开手掌,墨色风涡从掌心升起,卷动几张轻薄奏章纸页,但不形成暴风。墨风沿手腕和毛笔方向流动,第三秒另一只手稳稳落笔;新写出的墨色笔画从纸面升起,连续化成微缩宫阙与城墙轮廓。后段镜头随墨风越过城楼,吹向远处夜色,表达她的笔已经进入江山叙事。",
transition="远处墨云缓慢聚拢成祖父的白发面容,进入Bridge。"),
dict(n=18, section="Bridge", title="比报仇更长的一句咒", lyric="(古琴独白)祖父啊 我没替你报仇 / 我活成了 比报仇 更长的一句咒",
beats=[("0–3秒", "古琴独白中,祖父面容从砚中缓慢浮现。"), ("3–7秒", "婉儿凝视祖父,宫城与诏书片段在墨面掠过。"), ("7–10秒", "祖父面容与婉儿倒影重合,烛泪落入砚池。")],
subtitles="0.5–4.5秒「祖父啊 我没替你报仇」;4.5–9.8秒「我活成了 比报仇 更长的一句咒」",
prompt="古琴独白氛围,中近景缓推。成年婉儿俯身凝视砚中墨面,白发祖父上官仪的清瘦威严面容从涟漪中缓慢浮现。墨面边缘掠过焚宅、诏书、宫城和搀扶帝王的淡淡片段,如漫长人生回声。第七秒祖父面容与婉儿自己的倒影短暂重合,一滴烛泪落入砚池,重合影像被涟漪打散。婉儿唇角微颤但不落泪。",
transition="涟漪散尽后只留下婉儿自己的倒影,进入自我回答。"),
dict(n=19, section="Bridge", title="只是想活下去的人", lyric="或者 只是一个 想活下去的人",
beats=[("0–3秒", "黑镜只映出婉儿自己,她第一次正视倒影。"), ("3–7秒", "她直起身,身后宫门缓慢打开,冷夜转为黎明。"), ("7–10秒", "她走向门口但停在光影交界,留下选择余地。")],
subtitles="0.8–9.5秒「或者 只是一个 想活下去的人」;30pt淡灰,像低声自语",
prompt="砚面恢复平静,只映出成年婉儿自己的脸,不再出现祖父或帝王。她与倒影对视约三秒,随后缓慢直起身。身后半闭宫门逐渐打开,门外不是炫目强光,而是冷灰黎明与淡金天色。婉儿向门口走两步,停在阴影与晨光交界处,侧脸安静、疲惫但仍有生命力。镜头不煽情,表达她也可能只是一个努力活下来的人。",
transition="门外淡金天色叠化为明黄诏书纸面。"),
dict(n=20, section="Outro", title="墨干诏成,风仍在掌上", lyric="(研墨声渐远)墨干了 诏成了 / 风 还在掌上",
beats=[("0–3秒", "俯拍诏书,墨迹由湿转干,研墨声渐远。"), ("3–7秒", "墨字离纸化成宫阙,镜头升起俯瞰灯火。"), ("7–10秒", "婉儿在城楼摊开手掌,灯火逐盏熄灭,最终全黑。")],
subtitles="0.5–5.0秒「墨干了 诏成了」;5.0–9.1秒「风 还在掌上」;9.1秒后全黑",
prompt="俯拍明黄诏书,湿润墨迹在前三秒内缓慢失去反光并变干,研墨声逐渐远去。随后墨色笔画从纸面升起,化为微缩宫阙与大明宫夜景,镜头随之上升后拉。婉儿背影立在最高城楼,轻轻摊开一只手掌,掌心只剩一缕很淡的墨风。第七秒起万家灯火逐盏熄灭,最后仅剩她身旁一盏灯,约第九秒熄灭并归于全黑。生成完整10秒;合成199.152秒音频时裁掉末尾约0.85秒黑场。",
transition="全黑结束,不加片尾文字或Logo。"),
]
def tc_for(n):
start = (n - 1) * 10
end = n * 10
return f"{start // 60:02d}:{start % 60:02d}–{end // 60:02d}:{end % 60:02d}"
def add_cover(doc):
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(62)
p.paragraph_format.space_after = Pt(16)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
b.set_run_font(p.add_run("上官婉儿主题音乐视频"), size=11, bold=True, color=b.GOLD)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(8)
b.set_run_font(p.add_run("《我为你研墨》"), size=30, bold=True, color=b.INK, name=b.SERIF)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(10)
b.set_run_font(p.add_run("完整歌词脚本与分镜头提示词"), size=17, color=b.CINNABAR)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(30)
b.set_run_font(p.add_run("20 个分镜 × 每镜严格 10 秒|不生成图片"), size=11, bold=True, color=b.GOLD)
table = doc.add_table(rows=6, cols=2)
b.set_table_geometry(table, [1900, 7460])
b.set_repeat_table_header(table.rows[0])
for j, text in enumerate(("项目", "制作参数")):
b.set_cell_shading(table.cell(0, j), b.INK)
p = table.cell(0, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
b.set_run_font(p.add_run(text), size=9.2, bold=True, color=b.WHITE)
rows = [
("歌词结构", "Intro → Verse → Pre → Chorus → Verse → Pre → Chorus → Bridge → Outro"),
("分镜", "20镜;每条图生视频提示词生成时长固定为10秒"),
("素材总长", "200秒;与199.152秒音频合成时,裁掉最后约0.85秒黑场"),
("画幅", "9:16竖屏,1080×1920,H.264,30fps"),
("说明", "本文件只提供脚本、字幕与提示词,不生成任何图片或视频素材"),
]
for i, (label, value) in enumerate(rows, start=1):
b.set_cell_shading(table.cell(i, 0), b.PALE)
for j, text in enumerate((label, value)):
p = table.cell(i, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
b.set_run_font(p.add_run(text), size=9.4, bold=(j == 0), color=b.CINNABAR if j == 0 else b.INK)
doc.add_page_break()
def add_front(doc):
b.add_heading(doc, "1. 创作说明", 1)
b.add_label_para(doc, "核心叙事|", "不会写字的罪臣孤女,在仇人的宫廷中学会用笔;她替武则天书写江山,也借文字延长自己的生命,最终承认自己或许不是复仇者,只是一个想活下去的人。")
b.add_label_para(doc, "歌词原则|", "所有歌词严格按照用户提供的顺序出现;重复的Pre与Chorus采用不同情绪层级,第一次偏被动与撕裂,第二次偏清醒与主动。")
b.add_label_para(doc, "10秒规则|", "每个分镜提示词都明确要求生成10秒,不拆成5秒或8秒。20条素材合计200秒;最后一镜的末尾是黑场,可安全裁掉约0.85秒以匹配199.152秒音频。")
b.add_label_para(doc, "字幕规则|", "文档给出的是镜内建议区间。正式剪辑时,歌词字幕应以实际演唱咬字和音频波形为准,在相邻10秒镜头内前后微调,不需要改变画面提示词。")
b.add_heading(doc, "2. 统一视觉风格", 1)
b.add_prompt(doc, b.STYLE_PREFIX + "人物以成年上官婉儿为主,幼年与少年阶段只在明确镜头出现;武则天采用晚年帝王形象;全片不生成可辨读文字,字幕统一后期添加。")
b.add_heading(doc, "2.1 统一负面提示词", 2)
b.add_label_para(doc, "负面提示|", b.NEGATIVE, after=8, shade="F4EEEE")
b.add_heading(doc, "3. 20镜歌词分配总表", 1)
table = doc.add_table(rows=1, cols=5)
widths = [540, 1320, 930, 3710, 2860]
b.set_table_geometry(table, widths)
b.set_repeat_table_header(table.rows[0])
for j, text in enumerate(("镜", "时间", "段落", "歌词", "画面主题")):
b.set_cell_shading(table.cell(0, j), b.INK)
p = table.cell(0, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
b.set_run_font(p.add_run(text), size=8.8, bold=True, color=b.WHITE)
for s in SHOTS:
cells = table.add_row().cells
values = (str(s["n"]), tc_for(s["n"]), s["section"], s["lyric"].replace(" / ", "\n"), s["title"])
for j, text in enumerate(values):
p = cells[j].paragraphs[0]
p.paragraph_format.space_after = Pt(0)
p.paragraph_format.line_spacing = 1.05
p.alignment = WD_ALIGN_PARAGRAPH.CENTER if j < 3 else WD_ALIGN_PARAGRAPH.LEFT
for k, line in enumerate(text.split("\n")):
if k:
p.add_run().add_break()
b.set_run_font(p.add_run(line), size=8.2, bold=(j == 0), color=b.INK)
if s["n"] % 2 == 0:
b.set_cell_shading(cells[j], "FBF8F4")
b.set_table_geometry(table, widths)
b.add_label_para(doc, "音频收尾|", "第20镜生成00:10完整黑场尾帧;成片在03:19.152结束,裁掉最后约0.848秒黑场即可,不影响歌词和画面动作。", shade=b.PAPER)
doc.add_page_break()
def add_shot(doc, s):
h = doc.add_paragraph(style="Heading 1")
h.paragraph_format.page_break_before = True
h.paragraph_format.keep_with_next = True
b.set_run_font(h.add_run(f"分镜 {s['n']:02d}|{s['title']}"), size=16, bold=True, color=b.CINNABAR)
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(8)
b.shade_paragraph(p, b.PALE, b.GOLD)
for idx, (label, value) in enumerate((("时间", tc_for(s["n"])), ("生成", "严格10.00秒"), ("段落", s["section"]))):
if idx:
b.set_run_font(p.add_run(" | "), size=8.8, color=b.GOLD)
b.set_run_font(p.add_run(label + " "), size=8.8, bold=True, color=b.CINNABAR)
b.set_run_font(p.add_run(value), size=8.8, color=b.INK)
b.add_label_para(doc, "对应歌词|", s["lyric"], after=9, shade=b.PAPER)
b.add_heading(doc, "10秒导演脚本", 2)
for timing, action in s["beats"]:
b.add_label_para(doc, timing + "|", action, after=4)
b.add_heading(doc, "字幕建议", 2)
b.add_label_para(doc, "镜内字幕|", s["subtitles"], after=5)
b.add_label_para(doc, "转场|", s["transition"], after=7)
b.add_heading(doc, "10秒分镜提示词(可直接复制)", 2)
full_prompt = (
b.STYLE_PREFIX
+ "生成时长严格10秒,整个动作必须在0–10秒内完成;首帧和尾帧稳定可剪辑;不生成字幕、歌词、Logo或水印。"
+ s["prompt"]
+ "镜内节拍:"
+ ";".join(f"{timing}{action}" for timing, action in s["beats"])
+ "。"
)
b.add_prompt(doc, full_prompt)
b.add_label_para(doc, "统一负面提示|", b.NEGATIVE, after=7, shade="F4EEEE")
def add_end(doc):
h = doc.add_paragraph(style="Heading 1")
h.paragraph_format.page_break_before = True
b.set_run_font(h.add_run("4. 剪辑与验收"), size=16, bold=True, color=b.CINNABAR)
b.add_heading(doc, "4.1 剪辑执行", 2)
steps = [
("01|生成", "按分镜01至20顺序生成,每条时长选10秒;镜6左右分屏建议分别生成10秒再合成。"),
("02|铺音频", "先锁定《我为你研墨》原音频,再将20条素材按每10秒排列。"),
("03|字幕", "字幕不写入生成画面;以本文歌词顺序为准,以实际演唱波形微调入出点。"),
("04|收尾", "第20镜在03:19.152结束,裁掉素材末尾约0.848秒黑场。"),
("05|导出", "1080×1920,H.264,30fps,8–12Mbps;不添加未要求的片头、片尾或Logo。"),
]
for label, text in steps:
b.add_label_para(doc, label + " ", text, after=6)
b.add_heading(doc, "4.2 验收标准", 2)
table = doc.add_table(rows=1, cols=2)
b.set_table_geometry(table, [1800, 7560])
b.set_repeat_table_header(table.rows[0])
for j, text in enumerate(("验收项", "通过标准")):
b.set_cell_shading(table.cell(0, j), b.INK)
p = table.cell(0, j).paragraphs[0]
p.paragraph_format.space_after = Pt(0)
b.set_run_font(p.add_run(text), size=9.2, bold=True, color=b.WHITE)
rows = [
("提示词时长", "20条提示词均明确标注10.00秒,无5秒、7秒或16秒生成要求。"),
("歌词完整", "用户提供的全部歌词按原顺序出现,两次Pre和两次Chorus均完整保留。"),
("人物连续", "幼年、少年与成年婉儿阶段清晰;成年服装、红簪、银镯和右上烛光方向保持统一。"),
("画面安全", "无现代元素、可辨读文字、字幕、水印、Logo、畸形手指、穿模或面部突变。"),
("音频结尾", "最终成片在03:19.152结束,最后一句和研墨声尾音完整保留。"),
]
for label, value in rows:
cells = table.add_row().cells
b.set_cell_shading(cells[0], b.PALE)
for j, text in enumerate((label, value)):
p = cells[j].paragraphs[0]
p.paragraph_format.space_after = Pt(0)
b.set_run_font(p.add_run(text), size=9.2, bold=(j == 0), color=b.CINNABAR if j == 0 else b.INK)
b.set_table_geometry(table, [1800, 7560])
def build():
OUT.parent.mkdir(parents=True, exist_ok=True)
doc = Document()
sec = doc.sections[0]
sec.page_width = Inches(8.5)
sec.page_height = Inches(11)
sec.top_margin = Inches(1)
sec.bottom_margin = Inches(1)
sec.left_margin = Inches(1)
sec.right_margin = Inches(1)
sec.header_distance = Inches(0.492)
sec.footer_distance = Inches(0.492)
sec.different_first_page_header_footer = True
b.configure_styles(doc)
hp = sec.header.paragraphs[0]
b.set_run_font(hp.add_run("《我为你研墨》|完整歌词20镜执行稿"), size=8.5, color=b.MUTED)
b.add_page_field(sec.footer.paragraphs[0])
core = doc.core_properties
core.title = "《我为你研墨》完整歌词脚本与20镜分镜提示词(每镜10秒)"
core.subject = "上官婉儿主题音乐视频制作执行稿"
core.author = ""
core.keywords = "我为你研墨, 上官婉儿, 完整歌词, 20镜, 10秒, 分镜提示词"
core.comments = "根据用户提供完整歌词顺序创作,不生成图片。"
add_cover(doc)
add_front(doc)
for shot in SHOTS:
add_shot(doc, shot)
add_end(doc)
doc.save(OUT)
print(OUT)
if __name__ == "__main__":
build()
+79
View File
@@ -0,0 +1,79 @@
param(
[Parameter(Mandatory = $true)]
[string]$ProjectPath,
[Parameter(Mandatory = $true)]
[string]$ProjectName
)
$ErrorActionPreference = 'Stop'
$claudeExe = 'C:\Users\ZHIU\.local\bin\claude.exe'
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
$host.UI.RawUI.WindowTitle = "Claude Code - $ProjectName"
Clear-Host
Write-Host '========================================' -ForegroundColor DarkCyan
Write-Host " Claude Code:$ProjectName" -ForegroundColor Cyan
Write-Host '========================================' -ForegroundColor DarkCyan
Write-Host "项目目录:$ProjectPath"
Write-Host ''
if (-not (Test-Path -LiteralPath $claudeExe)) {
Write-Host '没有找到 Claude Code 程序。' -ForegroundColor Red
Read-Host '按回车关闭窗口'
exit 1
}
if (-not (Test-Path -LiteralPath $ProjectPath -PathType Container)) {
Write-Host '项目目录不存在。' -ForegroundColor Red
Read-Host '按回车关闭窗口'
exit 1
}
function Test-ClaudeLogin {
try {
$statusText = & $claudeExe auth status 2>$null | Out-String
$status = $statusText | ConvertFrom-Json
return [bool]$status.loggedIn
}
catch {
return $false
}
}
if (-not (Test-ClaudeLogin)) {
Write-Host '当前尚未登录 Claude。现在开始登录。' -ForegroundColor Yellow
Write-Host '请按屏幕提示操作;如果浏览器打开,请在浏览器中完成登录。'
Write-Host ''
& $claudeExe auth login
if (-not (Test-ClaudeLogin)) {
Write-Host ''
Write-Host '登录没有完成,Claude 聊天尚未启动。' -ForegroundColor Red
Write-Host '如果看到“所在地区不可用”,说明当前网络无法登录 Claude。'
Read-Host '按回车关闭窗口'
exit 1
}
}
Set-Location -LiteralPath $ProjectPath
Clear-Host
Write-Host "已进入项目:$ProjectName" -ForegroundColor Green
Write-Host '现在可以直接输入中文,例如:请先阅读这个项目并告诉我现在做到哪里。'
Write-Host '退出 Claude:输入 /exit,或按 Ctrl+C。'
Write-Host ''
& $claudeExe
$exitCode = $LASTEXITCODE
Write-Host ''
if ($exitCode -eq 0) {
Write-Host 'Claude 会话已结束。' -ForegroundColor Yellow
}
else {
Write-Host "Claude 已退出,错误代码:$exitCode" -ForegroundColor Red
}
Read-Host '按回车关闭窗口'
exit $exitCode
@@ -0,0 +1,87 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed sixth-to-early-seventh-century East Asia, cinematic 16:9 landscape, subtle film grain, physically plausible troops, terrain and logistics, rammed earth, aged timber, hemp, low-sheen silk, leather, iron lamellar armor, straight swords, bows, crossbows, spears, cavalry tack, wooden carts and period river craft. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, later Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Chen, Northern Zhou and Sui story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const person = (name, office, action) => ({ name, office, action })
const cue = (scene, from, to, direction) => ({ scene, from, to, direction })
const packs = [
{
dir: '陈/梁山之战(侯瑱大破王琳)',
event: { canonicalName: '梁山之战(侯瑱大破王琳)', aliases: ['梁山之战', '侯瑱破王琳', '平王琳之战'], projectDynasty: '陈', era: '陈文帝天嘉元年、北齐乾明元年', date: '公元560年二月至三月', place: '长江梁山、博望一带', summary: '王琳拥立萧庄并联合北齐水陆军东进,陈将侯瑱在梁山一带击破其军,随后再败北齐援军;王琳与萧庄逃往北齐,陈朝由此度过立国后的重大危机。' },
narration: { language: 'zh-CN', estimatedSeconds: 84, fullText: '陈朝建立不久,长江上游仍在王琳控制之下。王琳拥立梁永嘉王萧庄,与建康的陈文帝分庭抗礼,又得到北齐军队支援。天嘉元年,王琳率水军沿江东下,北齐兵从北岸配合,目标直指陈朝腹地。陈军由太尉侯瑱统率,在梁山、博望一带扼守江面。双方对峙时,决定胜负的不只是船多船少,而是谁能稳住水陆协同、不给对方突破江防的机会。交战之后,王琳军败,舰船与队形瓦解;侯瑱随即转兵,再次击退北齐援军。王琳与萧庄无法在江南立足,只得逃往北齐。梁山之战没有结束南北对峙,却清除了陈朝上游最危险的割据政权,使建康朝廷第一次真正控制长江中下游。对陈文帝而言,这不是一次普通的水战胜利,而是新王朝能否继续存在的生死关口。' },
scenes: [
{ id: '01', title: '王琳东下·两军夹江', timePlace: '长江中游至梁山水道', purpose: '交代王琳、萧庄与北齐援军构成的多方威胁。', narration: '王琳拥萧庄自上游东下,北齐军从北岸接应,水陆两路共同逼近陈境。', overlay: { heading: '新朝未稳', line: '上游有另一座朝廷,北岸有齐军,建康面对的不是一支孤军。' }, people: [person('王琳', '梁湘州刺史、割据将领', '拥立萧庄并率水军东下'), person('萧庄', '梁永嘉王、王琳所立皇帝', '随军东进,作为政权号召'), person('北齐援军', '北齐水陆部队', '从北岸配合王琳军行动')], image: image('01-wang-lin-fleet-descends-yangtze', '早春长江宽阔水面,王琳的木质楼船与中小战船顺流东下,岸上北齐步骑沿堤推进;萧庄乘坐的主船旗帜无字,船队规模真实克制。', 'Early spring on the broad Yangtze. Wang Lin’s mixed fleet of wooden command ships and smaller oared war boats descends east while a separate Northern Qi infantry-and-cavalry column advances along the north bank. Xiao Zhuang remains aboard a restrained command vessel under blank standards. Show a fragile coalition and converging pressure, not one unified armada, giant fantasy ships or a modern naval formation.', '具体船阵、萧庄所在船只与岸军距离为视觉重构;王琳奉萧庄并获北齐支援东下有史载。'), sources: ['《陈书·世祖纪》卷三', '《资治通鉴》卷一百六十八'] },
{ id: '02', title: '侯瑱扼江·梁山列阵', timePlace: '梁山、博望江段', purpose: '建立陈军以江防阻住东进的战场关系。', narration: '侯瑱率陈军守住梁山水道,水军在江心列阵,岸兵控制渡口与山脚。', overlay: { heading: '守住这段江', line: '王琳只要冲过梁山,建康上游就再没有同样坚固的屏障。' }, people: [person('侯瑱', '陈太尉、都督水陆诸军事', '统率水陆军扼守梁山'), person('陈军舟师', '水军', '以大小舰船封锁主航道'), person('陈军岸兵', '弩兵与步卒', '控制渡口并支援江面')], image: image('02-hou-tian-holds-liangshan-channel', '梁山江段清晨薄雾,侯瑱立于低矮木质指挥船上观察水流,陈军大小战船错位横列,岸边弩兵守住渡口和山脚;不画现代意义封锁线。', 'Morning mist at the Liangshan reach. Hou Tian stands on a low, practical wooden command boat studying current and distance. Chen war craft form a staggered defensive line while crossbow infantry secure a landing place and the foot of the hills. Emphasize coordinated river defense and restrained scale; no literal tactical arrows, chain boom unless visible only as ordinary mooring rope, or implausible cliff fortress.', '舰船错位、指挥船位置与岸兵部署为视觉推演;侯瑱统率水陆军在梁山迎战有史载。'), sources: ['《陈书·世祖纪》卷三', '《陈书·侯瑱传》'] },
{ id: '03', title: '江面决战·王琳军溃', timePlace: '梁山附近长江水面', purpose: '呈现王琳船队失去队形,而不虚构单一奇谋。', narration: '两军在梁山江面交战。王琳军队形被打乱,船只后撤相撞,水陆协同随之崩解。', overlay: { heading: '上游之军折返', line: '不是一艘船决定胜负,而是整条战线先失去了彼此照应。' }, people: [person('侯瑱', '陈军主帅', '维持水陆军协同并组织推进'), person('王琳', '梁军主帅', '在败势中收拢残部撤退'), person('双方舟师军士', '桨手、弩手与甲士', '在近距离箭战和接舷威胁下调整船位')], image: image('03-river-battle-breaks-wang-lin-formation', '梁山江面风浪中,陈军战船保持斜列推进,王琳船队几艘中型木船在转向后彼此拥挤,桨手和弩手努力恢复秩序;岸上两军仍在牵制,不画燃烧舰队或落水尸体。', 'During the Liangshan river battle, Chen boats retain a staggered advancing line while several of Wang Lin’s medium wooden vessels crowd one another during a failed turn. Oarsmen strain and crossbow crews seek targets; troops on both banks remain engaged at a distance. Show formation breakdown rather than a single invented trick. No fire attack, explosive ships, drowned bodies or impossible naval rams.', '具体阵形、转向拥挤和战斗距离为视觉重构;王琳军在梁山败绩有史载,未把胜负归于无据奇谋。'), sources: ['《陈书·世祖纪》卷三', '《资治通鉴》卷一百六十八'] },
{ id: '04', title: '再破齐军·王琳北逃', timePlace: '博望与北岸退路', purpose: '交代齐军亦败以及王琳、萧庄出走后的政治结果。', narration: '侯瑱乘胜击退北齐援军。王琳与萧庄离开江南逃往北齐,陈朝控制了长江下游。', overlay: { heading: '长江只剩一个朝廷', line: '王琳失去的既是船队,也是继续在江南拥立皇帝的立足点。' }, people: [person('侯瑱', '陈太尉', '乘胜转击北齐援军'), person('王琳', '战败将领', '与少数随从北逃'), person('萧庄', '王琳所立梁帝', '随王琳进入北齐')], image: image('04-wang-lin-and-xiao-zhuang-flee-north', '战后阴天的长江北岸,王琳与萧庄在少数护卫陪同下登岸北行,远处陈军船队控制江面并收拢缴获船只;画面不出现受降庆典或文字旗号。', 'Under a gray post-battle sky on the north bank, Wang Lin and Xiao Zhuang leave the river with only a small mounted escort, looking back at Chen vessels controlling the channel and collecting abandoned craft. Hou Tian is suggested aboard the distant fleet, not supernaturally present in close-up. Convey political exile and a secured river; no humiliating parade, corpses or triumphal palace.', '北逃路线、回望与收拢船只为视觉转译;王琳、萧庄奔北齐及侯瑱再破齐军有史载。'), sources: ['《陈书·世祖纪》卷三', '《资治通鉴》卷一百六十八'] },
],
bgm: { catalogId: 'zhanzhen:chibi#0', title: '赤壁之战', version: '电影级配乐·水战结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '仅借低弦、桨节奏和水战推进结构;删除赤壁专属火攻音效、人声、歌词与品牌尾签,梁山决战只形成一次短峰。', reason: '项目暂无梁山专曲,赤壁器乐结构能支撑水陆协同叙事;但本战无据可写大规模火攻,因此明确删除对应音色。仓库暂无成品音频。', cues: [cue('01','00:00','00:21','低桨节奏呈现两路逼近。'), cue('02','00:21','00:42','陈军列阵,低弦收紧。'), cue('03','00:42','01:03','江面失序形成唯一短峰。'), cue('04','01:03','01:24','鼓点退去,以冷弦结束北逃。')] },
sources: [{ claim: '王琳东下、侯瑱破琳与萧庄奔齐', citation: '《陈书·世祖纪》卷三', url: 'https://zh.wikisource.org/zh-hans/陈书/卷03' }, { claim: '梁山战事及同年南北形势日序', citation: '《资治通鉴》卷一百六十八', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷168' }, { claim: '侯瑱统军事迹', citation: '《陈书·侯瑱传》', url: 'https://zh.wikisource.org/zh-hans/陈书/卷09' }],
blacklist: ['把王琳、萧庄与北齐军合并成完全统一的一支军队', '虚构铁索封江、火攻连环船或单骑斩帅决定胜负', '把萧庄画成战场前锋或把王琳写成北齐将领', '把战后控制长江等同于陈朝已经统一整个南方', '现代文字、水印、后世服甲建筑、火器、尸堆与血腥'],
},
{
dir: '陈/吕梁之战(吴明彻兵败被俘)',
event: { canonicalName: '吕梁之战(吴明彻兵败被俘)', aliases: ['吕梁之败', '吴明彻北伐之败', '清口之战'], projectDynasty: '陈', era: '陈宣帝太建九年至十年、北周武帝建德六年至宣政元年', date: '公元577年至578年二月', place: '徐州吕梁、清口及淮泗一带', summary: '陈将吴明彻乘北齐衰亡之际北伐,先在吕梁击退周军,后因北周增援与水陆形势转变而陷入被动;公元578年陈军大败,吴明彻及大批将卒被俘,陈朝由攻转守并逐渐失去淮南。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '太建九年,北周灭北齐,淮泗以北的力量格局突然改变。陈宣帝仍命吴明彻继续北进,希望在北周完全接收齐地以前扩大边界。吴明彻是陈朝最有经验的将领之一,起初在吕梁击退北周军,围攻徐州,战局看似仍有机会。但陈军深入北方,兵力、粮道与水路都承受更大压力;北周则能不断调集援军,重新组织防线。太建十年二月,双方在吕梁、清口一带决战。陈军水陆联系被切断,撤退通道逐渐收窄,阵列终于崩溃。吴明彻没有成功退回淮南,与许多将卒一起被北周俘获。史书用“众军败绩”和“将卒并为周军所获”概括这一结局,却也意味着陈朝失去了北伐主力。此后,陈军从争夺淮北转为保守江淮,北周及后来的隋取得主动。吕梁之败不是一名老将突然失去勇气,而是陈朝在北方统一后仍继续深入用兵,最终遭遇的战略反转。' },
scenes: [
{ id: '01', title: '齐亡之后·北伐未止', timePlace: '吕梁、徐州外围', purpose: '建立北齐已亡而陈军仍继续深入的战略背景。', narration: '北周灭北齐后,吴明彻仍率陈军围攻徐州,陈军面对的对手已经从残齐变成统一北方的周军。', overlay: { heading: '对手已经变了', line: '地图上的城池还在,背后支撑它们的北方政权却完成了合并。' }, people: [person('吴明彻', '陈司空、北伐主帅', '率军在吕梁、徐州方向继续作战'), person('陈宣帝陈顼', '陈朝皇帝', '支持继续北伐并争夺淮泗'), person('北周徐州守军', '城防部队', '等待北方援军到达')], image: image('01-chen-army-besieges-xuzhou-after-qi-falls', '寒春徐州外围,老将吴明彻在陈军营前观察低矮夯土城,粮车和河船从身后长线抵达;北方地平线上出现北周增援尘线,陈军仍维持围城秩序。', 'In cold early spring outside a compact rammed-earth Xuzhou, the elderly general Wu Mingche studies the defenses from an orderly Chen camp. A long, vulnerable chain of grain carts and shallow river transports approaches behind him while a distant Northern Zhou relief dust line appears from the north. Show an army still confident but strategically extended, not a literal map, giant metropolis or already-collapsed camp.', '围城营地、粮道长度与援军尘线为视觉重构;吴明彻在北齐灭亡后继续围攻徐州有史载。'), sources: ['《陈书·宣帝纪》卷五', '《资治通鉴》卷一百七十三'] },
{ id: '02', title: '吕梁初胜·攻势延长', timePlace: '吕梁河道与徐州南面', purpose: '交代577年陈军曾获胜,避免把战役画成一开始便注定覆没。', narration: '吴明彻先在吕梁击退梁士彦等周军,陈军因此继续保持对徐州的压力。', overlay: { heading: '先胜而后败', line: '前一次击退援军,让深入的战线看上去仍然值得维持。' }, people: [person('吴明彻', '陈军主帅', '在吕梁击退周军并继续围城'), person('梁士彦', '北周徐州总管', '出城迎战后退回防线'), person('陈军舟师与步军', '北伐主力', '沿水道保持补给与前线联系')], image: image('02-wu-mingche-wins-first-luliang-clash', '吕梁河岸,陈军盾弩步卒稳步推进,河中小型运输战船同步前移;北周梁士彦所部有序退向徐州外垒,吴明彻在岸上指挥,不画屠杀式追击。', 'At the Lüliang riverbank in 577, Chen shield-and-crossbow infantry advances in controlled ranks while small supply and war boats keep pace in the channel. Liang Shiyan’s Northern Zhou troops conduct an orderly retreat toward outer Xuzhou works; Wu Mingche directs the advance from the bank. Present a limited initial victory, no annihilation, celebratory banners, body piles or anachronistic grand canal.', '具体推进阵形、水陆同步和周军撤退次序为视觉推演;吴明彻先败梁士彦所部见项目朝代事件与相关纪传。'), sources: ['《陈书·宣帝纪》卷五', '《周书·梁士彦传》'] },
{ id: '03', title: '周援压境·水陆断联', timePlace: '吕梁、清口水网', purpose: '表现北周增援和陈军退路收窄,不虚构堰水奇观。', narration: '北周增援抵达,陈军的水陆联系和粮道受到压迫。前线还能作战,退路却越来越窄。', overlay: { heading: '胜局开始缺粮', line: '当增援从北方不断到来,陈军每向前一步,都要用更长的道路把自己接回江淮。' }, people: [person('王轨', '北周行军总管', '率援军组织对陈军的合围'), person('吴明彻', '陈军主帅', '试图保持水陆阵线并寻找退路'), person('陈军转运部队', '舟师、役夫与护粮兵', '转移粮袋、牲畜与伤员')], image: image('03-zhou-relief-cuts-chen-routes', '阴雨清口水网,北周军在远处占据堤岸和渡口,陈军船队被迫挤入狭窄支流,岸上役夫搬运粮袋、士卒掩护撤运;吴明彻观察正在收窄的通道。', 'In rainy waterways near Qingkou, Northern Zhou forces secure levees and landing points in the middle distance. Chen transports crowd into a narrowing side channel while laborers shift grain sacks and soldiers cover the movement; Wu Mingche studies the shrinking route. Show logistical pressure and converging forces without inventing a giant dam, sudden supernatural flood, iron chain trap or modern canal locks.', '渡口占领、船队拥挤与搬粮细节为视觉重构;北周增援后陈军水陆联系恶化、退路受压为战局转折。'), sources: ['《陈书·宣帝纪》卷五', '《资治通鉴》卷一百七十三'] },
{ id: '04', title: '众军败绩·主帅被俘', timePlace: '吕梁陈军撤退线', purpose: '交代战败与被俘,并以放下武器代替直接杀戮。', narration: '陈军撤退失败,吴明彻与大批将卒被俘。失去主力后,陈朝北伐转为江淮防御。', overlay: { heading: '北伐止于吕梁', line: '老将没有带回这支军队,陈朝也再没有同样的力量向北推进。' }, people: [person('吴明彻', '陈司空', '战败被俘,保持将领身份'), person('北周军士', '受降与押送部队', '收缴陈军武器并看守俘虏'), person('陈军残部', '北伐将卒', '放下武器或分散退向南方')], image: image('04-wu-mingche-captured-after-defeat', '雨后昏暗的吕梁营地,吴明彻站在已放下长槊和弩机的陈军将卒前,北周军士有序收缴武器;远处少量陈军沿泥路南退,不画捆缚羞辱、斩首或尸体。', 'After rain in the dim Lüliang camp, Wu Mingche stands upright before exhausted Chen soldiers who have laid down spears and crossbows. Northern Zhou troops methodically collect weapons and guard the surrendered group while a few scattered Chen survivors withdraw south on a muddy road. Keep dignity and strategic loss central; no kneeling humiliation, chains as spectacle, execution, gore or body piles.', '受降位置、武器堆放与残部南退为视觉转译;吴明彻及将卒为周军所获、陈朝失去北伐主力有史载。'), sources: ['《陈书·宣帝纪》卷五', '《资治通鉴》卷一百七十三'] },
],
bgm: { catalogId: 'zhanzhen:tumu#0', title: '土木堡之变', version: '电影级配乐·远征溃败结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删去明代步伐与所有人声,只留冷弦、低频和逐层失序的打击;初胜不作英雄高潮,被俘处抽空为单音。', reason: '项目暂无吕梁专曲,土木堡配乐的“深入—补给恶化—全军溃败”器乐弧线可借用;必须移除明代叙事音色。仓库暂无成品音频。', cues: [cue('01','00:00','00:21','低弦拉出过长粮道。'), cue('02','00:21','00:42','初胜仅加一层短鼓。'), cue('03','00:42','01:04','节拍错位表现退路收窄。'), cue('04','01:04','01:26','骤停后以冷单音收尾。')] },
sources: [{ claim: '吕梁大败与吴明彻等被周军俘获', citation: '《陈书·宣帝纪》卷五', url: 'https://zh.wikisource.org/zh-hans/陈书/卷05' }, { claim: '陈周徐州、吕梁战局日序', citation: '《资治通鉴》卷一百七十三', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷173' }, { claim: '梁士彦守徐州相关事迹', citation: '《周书·梁士彦传》', url: 'https://zh.wikisource.org/zh-hans/周书/卷31' }],
blacklist: ['忽略北齐已亡这一战略背景,把战役继续写成陈齐战争', '把577年初胜与578年大败压成同一天的一场战斗', '虚构巨型水坝、铁索封河或神秘洪水决定胜负', '把吴明彻画成临阵投降或用受俘场面羞辱取乐', '现代文字、水印、后世服甲建筑、火器、尸堆与血腥'],
},
{
dir: '北周/灭北齐之战(晋州至邺城)',
event: { canonicalName: '灭北齐之战(晋州至邺城)', aliases: ['北周灭北齐之战', '周灭齐之战', '晋州之战', '北周统一北方'], projectDynasty: '北周', era: '北周武帝建德五年至六年、北齐后主武平七年至承光元年', date: '公元576年十月至577年正月', place: '平阳、晋州、晋阳、邺城及青州方向', summary: '北周武帝宇文邕亲征北齐,先取平阳、坚守晋州,再败北齐援军并攻入晋阳;北齐后主高纬弃城东逃,周军进入邺城,随后俘获高纬,北齐灭亡,北方大部归于北周。' },
narration: { language: 'zh-CN', estimatedSeconds: 88, fullText: '建德五年,北周武帝宇文邕再次亲征北齐。他没有直接扑向邺城,而是先攻平阳,切入齐国西部防线。北齐大军回援时,周军在晋州一带承受了最危险的反击;只要平阳失守,整场东征都可能重演此前的撤军。宇文邕亲临前线,整顿军阵,周军守住晋州并击退齐军。此后,战线迅速向东推进。周军攻入北齐别都晋阳,北齐后主高纬放弃继续组织防御,先后从晋阳、邺城向东逃走。建德六年正月,周军进入邺城。高纬试图奔往青州,途中被追兵俘获,北齐政权随即灭亡。《周书》记载,齐境五十五州、一百六十二郡与三百余万户纳入北周。数字背后,是北方两个长期对峙政权终于合并,也是一场皇帝亲征改变的权力版图。北周的统一并未维持很久,但它结束了东西政权分裂的主体格局,并为后来隋朝重新统一全国准备了北方基础。' },
scenes: [
{ id: '01', title: '武帝亲征·先取平阳', timePlace: '长安出师与平阳城外', purpose: '呈现北周选择西线要点切入,而非直接奇袭邺城。', narration: '宇文邕从长安亲征,周军越过黄河方向推进,首先夺取平阳,逼北齐主力西援。', overlay: { heading: '先撬开晋州', line: '通往邺城的路没有直走,周军先把北齐的援兵吸到平阳。' }, people: [person('宇文邕', '北周武帝', '亲率诸军东征并指挥攻取平阳'), person('梁士彦', '北周将领', '受命守卫新取的平阳'), person('北周诸军', '步骑与转运部队', '从关中向河东集结')], image: image('01-yuwen-yong-marches-on-pingyang', '深秋河东道路,宇文邕穿实用甲胄骑马随军,步兵、骑兵和粮车分层向平阳夯土城推进;梁士彦在前方查看新设防垒,旗帜无字。', 'Late autumn on the Hedong road. Emperor Yuwen Yong rides in practical campaign armor among infantry, cavalry and spaced supply carts advancing toward compact rammed-earth Pingyang. Liang Shiyan inspects newly occupied defensive works ahead. Make the emperor a working field commander within the army, not a golden-robed superhero; no literal map, giant banners or already-visible Ye in the distance.', '人物位置、行军层次与平阳外垒为视觉重构;武帝亲征、攻取平阳并留梁士彦守城有史载。'), sources: ['《周书·武帝纪下》卷六', '《资治通鉴》卷一百七十二'] },
{ id: '02', title: '晋州危急·周军稳阵', timePlace: '晋州、平阳城外', purpose: '表现北齐主力回援与北周守住关键节点。', narration: '北齐后主率军西援,平阳一度危急。宇文邕重整军阵,周军守住晋州并击退齐军。', overlay: { heading: '最险的一日', line: '先取平阳只是开门,能不能守住,才决定东征是否继续。' }, people: [person('宇文邕', '北周武帝', '临阵整军并督战'), person('高纬', '北齐后主', '率北齐主力驰援平阳'), person('梁士彦', '北周晋州守将', '凭城坚持等待主力')], image: image('02-jinzhou-line-holds-against-qi-relief', '冬日平阳夯土城外,北齐骑兵和步卒从开阔地压近,北周盾弩阵在城前重新闭合,宇文邕在后方亲自整队,梁士彦守军从城门接应;不画一人冲散万人。', 'Winter outside Pingyang. Northern Qi cavalry and infantry press across open ground while a Northern Zhou shield-and-crossbow line closes gaps before the rammed-earth city. Yuwen Yong personally restores order just behind the line; Liang Shiyan’s garrison supports from the gate. Show a hard-fought stabilization, not one hero routing an army, clone troops, giant cavalry collision or gore.', '阵线位置、皇帝整队和城门接应为视觉推演;晋州危急、周军守城并击退齐援有史载。'), sources: ['《周书·武帝纪下》卷六', '《北齐书·后主纪》卷八'] },
{ id: '03', title: '晋阳失守·后主东逃', timePlace: '晋阳至邺城道路', purpose: '交代齐军防线瓦解和高纬连续放弃都城。', narration: '周军乘胜攻入晋阳。高纬撤向邺城后又继续东逃,北齐中枢失去稳定指挥。', overlay: { heading: '都城在身后关闭', line: '每一次东逃都在争取时间,也让下一座城更早失去主心骨。' }, people: [person('高纬', '北齐后主', '离开晋阳并继续从邺城东逃'), person('高延宗', '北齐安德王', '在晋阳被拥立并组织短暂抵抗'), person('北周前锋', '追击部队', '占领晋阳后沿驿路向邺城推进')], image: image('03-gao-wei-flees-east-from-jinyang', '雪后驿路上,高纬的克制车骑队伍离开晋阳向东,车上无华丽龙纹;远处北周前锋进入夯土城门,城中另有高延宗部匆忙整队,不把不同时间画成魔法分屏。', 'On a snow-dusted road east of Jinyang, Gao Wei’s restrained court caravan departs without imperial-yellow spectacle. Far behind, Northern Zhou vanguard units enter a rammed-earth gate while Gao Yanzong’s defenders form within the city. Use depth to compress consecutive events honestly, not a magical split screen; no clownish decadence, massacre or ornate later palace convoy.', '车驾规模、城内整队与纵深压缩为视觉重构;高纬弃晋阳东走、高延宗短暂守城及周军入晋阳有史载。'), sources: ['《北齐书·后主纪》卷八', '《周书·武帝纪下》卷六'] },
{ id: '04', title: '周军入邺·齐主被俘', timePlace: '邺城与通往青州的道路', purpose: '交代北齐灭亡和北方版图重组,避免把入城与被俘混成同一现场。', narration: '周军进入邺城;高纬在奔青州途中被俘。北齐州郡户口并入北周,北方主要政权归一。', overlay: { heading: '邺城换旗', line: '北齐亡于一连串失去的城,也亡于再也无法重建的指挥中心。' }, people: [person('宇文邕', '北周武帝', '进入邺城并接收齐境'), person('高纬', '北齐后主', '在东逃青州途中被俘'), person('邺城官吏与百姓', '降附者', '缴纳无字户籍木牍与仓府钥匙')], image: image('04-zhou-enters-ye-and-qi-registers-surrendered', '邺城城门内,宇文邕与北周军克制入城,北齐官吏在侧交出仓府钥匙和无字户籍木牍;远景驿路上高纬小队被追兵拦截,以空间分层表示后续被俘。', 'Inside Ye’s restrained rammed-earth gate, Yuwen Yong enters with disciplined Northern Zhou troops while Northern Qi officials surrender storehouse keys and blank household tallies. Far beyond the gate along an eastern road, Gao Wei’s small fleeing party is stopped by mounted pursuers, clearly a later event represented through distant visual compression. No readable surrender document, looting, triumphal humiliation or merged close-up scenes.', '入城受降、木牍与远景追获为跨时段视觉压缩;周军入邺、高纬奔青州途中被俘及齐境并入北周有史载。'), sources: ['《周书·武帝纪下》卷六', '《北齐书·后主纪》卷八'] },
],
bgm: { catalogId: 'zhanzhen:pingchen#0', title: '隋灭陈·平陈之战', version: '电影级配乐·统一战争结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删去江南水战音色与所有人声,只取从集结、破城到接收户籍的统一战争弧线;晋州段形成唯一高潮,入邺处转为克制低弦。', reason: '项目暂无北周灭齐专曲,平陈曲同属完成统一版图的战争叙事,器乐结构可复用;需避免提前暗示隋朝。仓库暂无成品音频。', cues: [cue('01','00:00','00:22','军队集结,低鼓东行。'), cue('02','00:22','00:45','晋州守线形成唯一高潮。'), cue('03','00:45','01:07','鼓点加速后突然抽空。'), cue('04','01:07','01:28','入邺转为低弦与木牍声。')] },
sources: [{ claim: '武帝亲征、平晋阳、入邺与灭齐结果', citation: '《周书·武帝纪下》卷六', url: 'https://zh.wikisource.org/zh-hans/周书/卷06' }, { claim: '高纬自晋阳、邺城东逃及被俘', citation: '《北齐书·后主纪》卷八', url: 'https://zh.wikisource.org/zh-hans/北齐书/卷08' }, { claim: '晋州攻守及灭齐日序', citation: '《资治通鉴》卷一百七十二', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷172' }],
blacklist: ['把北周军从长安直接瞬移到邺城,省略平阳、晋州与晋阳', '把高纬简单漫画化为战场小丑,忽略军政指挥连续瓦解', '把高延宗在晋阳的短暂抵抗写成成功复国', '把北周灭齐等同于隋朝已经建立或全国已经统一', '现代文字、水印、后世服甲建筑、火器、尸堆与血腥'],
},
{
dir: '隋/第一次征高丽(萨水九军覆没)',
event: { canonicalName: '第一次征高丽(萨水九军覆没)', aliases: ['隋第一次征高丽', '隋炀帝第一次征高丽', '大业八年征高丽', '萨水之战'], projectDynasty: '隋', era: '隋炀帝大业八年、高句丽婴阳王二十三年', date: '公元612年正月至七月', place: '涿郡、辽河、辽东城、平壤与萨水一线', summary: '隋炀帝征发一百一十三万余军队第一次远征高句丽,诸军渡辽、围辽东后又派九军深入平壤;粮尽撤退时在萨水遭击而崩溃,九军损失惨重,远征失败并加剧隋末社会危机。' },
narration: { language: 'zh-CN', estimatedSeconds: 89, fullText: '大业八年,隋炀帝杨广在涿郡集结大军征讨高句丽。《隋书》记载,战兵一百一十三万三千八百,号称二百万,运送粮械的人员还要更多。各军依次出发,前后用了四十天,旌旗绵延近千里。声势越大,供给就越困难。隋军渡辽河时,麦铁杖率先登岸,战死于前锋;主力随后围攻辽东城,却受到皇帝必须受降、不得擅自处置的命令牵制,多次失去战机。为了直取平壤,宇文述、于仲文等率九军深入。士兵既要带兵器,又要背负百日粮,很多人暗中丢弃粮袋。九军抵近平壤后已经难以久留,只能撤退。高句丽军沿途追击,在萨水一带发动攻击,撤军秩序彻底崩溃。九军出发三十万五千人,史书记载回到辽东城的只有二千七百人。这个数字不必画成尸山,它首先意味着一个庞大后勤体系的失败。第一次征高丽没有迫使高句丽投降,却让民役、运输和军队损失压向隋朝社会,成为王朝走向崩解的重要节点。' },
scenes: [
{ id: '01', title: '涿郡集结·四十日出师', timePlace: '涿郡军营与北上道路', purpose: '用行军与后勤规模呈现远征的制度压力。', narration: '一百一十三万余战兵依次离开涿郡,四十天才全部出发,粮车与役夫构成更长的队伍。', overlay: { heading: '百万之师', line: '数字越壮观,每天要送到前线的粮食就越接近不可能。' }, people: [person('杨广', '隋炀帝', '在涿郡总领远征并规定诸军次序'), person('隋军将卒', '十二军与诸道部队', '按番号分批北上'), person('转运役夫', '征发民夫与车船人员', '运送粮食、箭矢、甲械与草料')], image: image('01-massive-sui-mobilization-at-zhuojun', '涿郡外广阔平原,隋军不是挤成一团,而是多列步兵、骑兵、粮车和役夫向地平线分批出发;杨广在朴素高台查看无字军册,近景可见称量粮袋。', 'On the vast plain outside Zhuojun, Sui forces depart in separated columns: infantry, cavalry, grain carts and laborers stretching toward the horizon rather than an impossible wall of people. Emperor Yang Guang observes from a restrained timber platform beside blank military tallies; workers weigh grain sacks in the foreground. Convey scale through depth and logistics, no literal millions, aerial satellite view or golden imperial spectacle.', '行军密度、高台、称粮与多列纵深为视觉转译;兵数、四十日出师及旌旗千里有《隋书》明确记载。'), sources: ['《隋书·炀帝纪下》卷四', '《资治通鉴》卷一百八十一'] },
{ id: '02', title: '辽河先登·麦铁杖战死', timePlace: '辽河西岸与东岸滩头', purpose: '呈现渡河代价和前锋将领战死,不直接展示死亡。', narration: '隋军架桥渡辽。麦铁杖率前锋先登东岸,与高句丽守军交战后战死,主力才逐步建立渡口。', overlay: { heading: '第一座河就见血', line: '大军还没有看见平壤,渡辽的第一批人已经回不来了。' }, people: [person('麦铁杖', '右屯卫大将军', '率前锋先渡辽水,力战而死;不展示遗体'), person('钱士雄、孟金叉', '隋军将领', '随前锋渡河作战并战死;不展示遗体'), person('高句丽守军', '辽河东岸防军', '在滩头阻击架桥和登陆')], image: image('02-mai-tiezhang-leads-liao-river-crossing', '早春辽河冷水中,隋军工兵架设简易浮桥,麦铁杖率第一批甲士踏上东岸泥滩,在高句丽弓兵箭雨下结成盾阵;断裂桥板与回撤小舟暗示代价,不画死亡瞬间。', 'At the cold Liao River in early spring, Sui engineers lash a practical pontoon bridge while Mai Tiezhuang leads the first armored infantry onto the muddy east-bank beach. Goguryeo archers defend from an earthen embankment; the landing party forms shields under arrows. Broken bridge planks and a returning skiff imply the deadly cost without showing Mai’s death, gore, floating bodies or later stone fortress imagery.', '浮桥形制、盾阵、守军位置与断板为视觉重构;麦铁杖先登辽水战死有史载。'), sources: ['《隋书·炀帝纪下》卷四', '《隋书·麦铁杖传》'] },
{ id: '03', title: '九军深入·百日粮尽', timePlace: '辽东至平壤道路', purpose: '把失败原因落在负重、补给与命令体系,而非天罚。', narration: '宇文述、于仲文等率九军深入,士卒背负百日粮。大量粮袋途中被弃,抵近平壤时已无力久战。', overlay: { heading: '粮食先倒下', line: '每个人多背一袋粮,军队就慢一点;每个人少一袋粮,平壤就远一点。' }, people: [person('宇文述', '左翊卫大将军', '统率九军深入并在粮尽后主张撤退'), person('于仲文', '右翊卫大将军', '参与统军并与诸将意见不一'), person('隋军士卒', '深入部队', '背负军械和百日粮,途中疲惫减员')], image: image('03-nine-armies-exhaust-supplies-near-pyongyang', '夏季阴雨的北方山路,隋军长列士卒肩背武器和沉重粮袋,路旁散落被割开的空袋而非满地尸体;宇文述与于仲文站在泥泞岔路查看无字木牍,远处只是平壤方向低云。', 'On a rain-soaked summer road toward Pyongyang, a long Sui column carries weapons and visibly heavy grain sacks. Cut and abandoned empty bags lie beside the mud, not bodies. Yuwen Shu and Yu Zhongwen confer at a fork over blank wooden tallies while low clouds obscure the destination. Show exhaustion, command friction and lost supplies; no literal map arrows, modern backpacks, Goguryeo city already visible or supernatural storm.', '粮袋样式、岔路会议、天气与弃粮痕迹为视觉重构;九军负百日粮、士卒私弃粮和抵平壤后粮尽有史载。'), sources: ['《隋书·炀帝纪下》卷四', '《资治通鉴》卷一百八十一'] },
{ id: '04', title: '萨水退军·九军崩溃', timePlace: '萨水渡口至辽东城退路', purpose: '交代撤退遭击与巨大损失,避免神化洪水或展示尸山。', narration: '九军粮尽撤退,高句丽军沿途袭击。萨水附近退军失序,能返回辽东城者极少。', overlay: { heading: '回来二千七百人', line: '史书留下一个数字;道路上留下的,是被丢弃的车、甲和再也接不上的队伍。' }, people: [person('乙支文德', '高句丽大臣、统军者', '追击撤退的隋军并把握萨水战机'), person('宇文述、于仲文', '隋军统帅', '在溃退中收拢残部返回辽东'), person('隋军残部', '九军将卒', '涉水、弃车并分散撤回')], image: image('04-salsu-retreat-collapses', '暮色萨水浅滩,疲惫隋军争相涉水撤退,岸上遗留空粮车、盾牌和解下的甲片;高句丽军从远处高岸逼近,前景少数军官试图重建队列,不画巨浪、溺尸或尸山。', 'At dusk on the shallow Salsu crossing, exhausted Sui troops wade and retreat in broken groups, leaving empty grain carts, shields and discarded armor plates on the bank. Goguryeo troops approach from distant high ground while a few officers try to restore a line in the foreground. Express catastrophic collapse through abandoned logistics and separation, not a divine flood, giant wave, drowning spectacle, corpses or gore.', '浅滩形态、弃械与追军距离为视觉转译;九军粮尽退军、萨水附近遭击及仅少数返回有史载,不采纳神洪叙事。'), sources: ['《隋书·炀帝纪下》卷四', '《资治通鉴》卷一百八十一'] },
],
bgm: { catalogId: 'zhanzhen:anshicheng#0', title: '唐太宗征高句丽·安市城之战', version: '电影级配乐·辽东远征结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '保留辽东远征的苦役节奏、冷弦与撤军弧线;删除唐代专属段落、人声、歌词和品牌尾签,萨水段不加入神异洪水音效。', reason: '项目暂无大业八年征高丽专曲,安市城曲同属辽东远征与补给失败,地理和情绪结构最接近;仍需避免混淆隋唐两次战争。仓库暂无成品音频。', cues: [cue('01','00:00','00:22','夯土与车轮节奏表现动员。'), cue('02','00:22','00:43','渡辽加入短促弦乐峰值。'), cue('03','00:43','01:06','负重行军逐层压低。'), cue('04','01:06','01:29','萨水撤军崩解后只留风声。')] },
sources: [{ claim: '军队规模、四十日出师、渡辽与九军覆没', citation: '《隋书·炀帝纪下》卷四', url: 'https://zh.wikisource.org/zh-hans/隋书/卷04' }, { claim: '麦铁杖先登辽水战死', citation: '《隋书·麦铁杖传》', url: 'https://zh.wikisource.org/zh-hans/隋书/卷64' }, { claim: '九军负粮、撤退与萨水战局日序', citation: '《资治通鉴》卷一百八十一', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷181' }],
blacklist: ['把“百万大军”画成不可分辨的复制人海,忽略役夫、车船和分批出发', '把隋军与唐太宗安市城之战混为一次远征', '把失败只归因于杨广个人癫狂,忽略命令体系、补给和纵深', '把萨水之败画成神明制造的巨浪或现代大坝泄洪', '现代文字、水印、后世服甲建筑、火器、尸堆与血腥'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((item) => `${item.name}(${item.office};${item.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((item) => `| ${item.from}–${item.to} | ${item.scene} | ${item.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Chen, Northern Zhou and Sui core battle packages`)
@@ -0,0 +1,35 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..'),outRoot=path.join(root,'战役素材','东汉')
const BASE=`Premium live-action Chinese historical film still, archaeology-informed Eastern Han material culture, cinematic 16:9 landscape, subtle film grain, physically plausible scale, rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered iron lamellar armor, straight swords, crossbows, spears and wooden carts or boats. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, modern maps, UI arrows, firearms, cannons, gunpowder explosions, samurai armor, European plate, magic, anime, gore or body piles.`
const img=(file,alt,request,inference)=>({original:`images/${file}.png`,delivery:`images/${file}.webp`,alt,prompt:`Use case: historical-scene. Digital Me Eastern Han story frame. ${request} ${BASE}`,visualInference:inference,refinementPrompt:''})
const packs=[
{dir:'黄巾之乱(长社火攻至广宗平定)',event:{canonicalName:'黄巾之乱(长社火攻至广宗平定)',aliases:['黄巾起义','黄巾之乱','长社之战','广宗之战'],era:'东汉灵帝中平元年',date:'公元184年二月至十一月',place:'青徐幽冀荆扬兖豫八州、长社、广宗与下曲阳',summary:'张角三十六方同日起兵波及八州;皇甫嵩、朱儁先在长社以夜火破波才,皇甫嵩后在广宗斩张梁、下曲阳斩张宝,黄巾主力被平,但东汉地方军事化由此加速。'},narration:{language:'zh-CN',estimatedSeconds:88,fullText:'公元一八四年二月,张角原定三月发动起事,却因弟子唐周告密而提前传令。青、徐、幽、冀等八州三十六方同日举兵,以黄巾为标识,旬日之间州郡失据。朝廷解开党锢,派卢植、皇甫嵩、朱儁分道出征。朱儁先被波才击败,皇甫嵩退保长社,兵少而被围。波才部众依草结营,皇甫嵩判断可用风火。夜里大风,他命士卒束苇登城,锐士潜出围外,同时纵火大呼,城上举火响应,官军乘乱突击。骑都尉曹操恰好率援军赶到,与皇甫嵩、朱儁合兵,大破波才。此后官军追击颍川、汝南黄巾。十月,张角已经病死,皇甫嵩在广宗先闭营休兵,待张梁军稍懈,夜间整军,鸡鸣突袭,战至下午斩张梁。十一月又在下曲阳击破张宝。黄巾主力虽在一年内瓦解,州郡募兵、豪强聚众的闸门却再也没有关上。'},scenes:[
{id:'01',title:'三十六方同日起',timePlace:'公元184年二月,八州郡县',purpose:'呈现提前发动与广泛响应,不把三十六万做成一个密集人海镜头。',narration:'事机泄露,张角晨夜传令。三十六方以黄巾为标识,在八州同日而起。',overlay:{heading:'同日而起',line:'十余年秘密结社,在一夜之间变成八州军情。'},people:[{name:'张角',office:'太平道首领、天公将军',action:'事泄后提前传令诸方起兵'},{name:'黄巾部众',office:'三十六方徒众',action:'以黄色头巾为标识,同日攻取郡县'}],image:img('01-thirty-six-districts-rise','东汉县城外,多支衣着各异的民众队伍在不同道路汇合,少量人系素朴黄色头巾,张角只在一处乡间坛场向信众发出急令,不出现可读口号。','Early dawn outside an Eastern Han county town. Several distinct groups of peasants, healers, laborers and former soldiers converge from separate roads; some wear simple faded yellow headcloths as identification, varied rather than uniform. In one modest rural meeting ground Zhang Jue, a lean middle-aged East Asian religious organizer in plain yellow-brown robe, urgently dispatches mounted and foot messengers in different directions. Smoke rises near a distant administrative compound but no massacre. Scale through branching roads, not a cloned horde; no readable slogans.','坛场、传令方式、队伍构成与同框地理为视觉重构;事泄提前、三十六方、八州响应与皆著黄巾有史载。'),sources:['《后汉书·皇甫嵩朱儁列传》卷七十一','《后汉书·孝灵帝纪》卷八']},
{id:'02',title:'兵少困长社',timePlace:'五月前后,长社城',purpose:'建立朱儁先败与皇甫嵩退保小城、被波才围困。',narration:'朱儁先败,皇甫嵩退保长社。波才以众围城,守军兵少,军心惊惧。',overlay:{heading:'兵少困长社',line:'城外营帐连成草海,城内只剩一次出奇的机会。'},people:[{name:'皇甫嵩',office:'左中郎将',action:'退保长社,判断黄巾草营可用风火'},{name:'朱儁',office:'右中郎将',action:'前战失利后与皇甫嵩共同应敌'},{name:'波才',office:'颍川黄巾首领',action:'率众围困长社'}],image:img('02-changshe-surrounded','长社低矮土城上,皇甫嵩与朱儁俯看城外黄巾草木营地环绕,守卒在城墙后搬运箭束与水桶,远方波才军不作夸张人海。','From the low rammed-earth wall of Changshe, Huangfu Song, a composed East Asian commander in his mid-40s with narrow face, tied beard and dark Han lamellar armor under a dull red cloak, and Zhu Jun, a sturdy commander in his 40s wearing brown-black armor, survey encircling Yellow Turban camps made from dry grass, reed shelters and simple timber. Nervous defenders carry arrow bundles and water jars behind the parapet. The camps visibly depend on dry vegetation; Bo Cai remains a distant leader, no invented portrait close-up.','城墙、营帐范围、人物外貌与守城准备为视觉重构;朱儁先败、嵩保长社、波才围城与兵少有史载。'),sources:['《后汉书·皇甫嵩朱儁列传》卷七十一']},
{id:'03',title:'夜风纵火',timePlace:'长社,夜间大风',purpose:'表现束苇举火、锐士潜出和城内外响应的战术节点。',narration:'夜里大风,守军束苇登城,锐士潜出围外。火从草营多处同时燃起。',overlay:{heading:'夜风纵火',line:'黄巾依草结营,风把营垒本身变成了破口。'},people:[{name:'皇甫嵩',office:'左中郎将',action:'部署束苇举火与潜出锐士'},{name:'长社守军',office:'官军五校、三河骑士及募兵',action:'城上举火、城外纵火并突击'}],image:img('03-night-fire-breaks-grass-camp','大风夜,城上守卒举起成束燃苇,城外锐士从暗处点燃黄巾草营的几处边缘,皇甫嵩在城门内等候出击;火势受控、不烧人群。','Windy night at Changshe. Defenders atop the earth wall raise bundles of burning reeds as signals while small elite teams outside the encirclement ignite several edges of the dry Yellow Turban grass camp. Huangfu Song, matching scene 02, waits inside the opened gate with shield infantry for the coordinated sortie. Gust-driven sparks and multiple low fires create sudden confusion without showing people burning or a fantasy inferno. Cold moonlight and controlled orange fire, no explosion.','燃点、潜出路线、开门时机与人物位置为视觉推演;夕夜大风、束苣乘城、锐士间出、纵火大呼有史载。'),sources:['《后汉书·皇甫嵩朱儁列传》卷七十一']},
{id:'04',title:'三军合破波才',timePlace:'长社城外,火起后',purpose:'呈现曹操援军恰至与皇甫嵩、朱儁合兵,而非曹操独胜。',narration:'皇甫嵩乘乱奔击,曹操援军恰至。三军合兵再战,大破波才。',overlay:{heading:'三军合破',line:'援军抵达得正是时候,但破围不是一个人的功劳。'},people:[{name:'皇甫嵩',office:'左中郎将',action:'率守军出城击阵'},{name:'朱儁',office:'右中郎将',action:'与皇甫嵩、曹操合兵再战'},{name:'曹操',office:'骑都尉',action:'奉命率兵适时赶到长社'}],image:img('04-reinforcements-arrive-at-changshe','黎明烟雾中,皇甫嵩与朱儁从长社城门整队推进,年轻曹操率小规模骑兵援军从侧翼抵达,黄巾队伍从燃烧草营间撤退;三位将领都不过度英雄化。','Smoke-gray dawn after the fires. Huangfu Song and Zhu Jun lead ordered shield and crossbow units out from Changshe while a younger Cao Cao, early 30s with compact build, alert eyes, short moustache, plain cavalry officer armor and no regal styling, arrives from the flank with a modest mounted reinforcement. Yellow Turban formations withdraw between damaged grass camps. Make the victory visibly combined, not Cao Cao alone; no duel, no gore, no giant cavalry charge.','三军会合位置、曹操外貌、推进队形与撤退路径为视觉重构;曹操将兵适至、嵩操与朱儁合兵大破有史载。'),sources:['《后汉书·皇甫嵩朱儁列传》卷七十一']},
{id:'05',title:'鸡鸣破广宗',timePlace:'十月,广宗;结尾延伸至十一月下曲阳',purpose:'交代张角病死、皇甫嵩待懈夜袭张梁与主力平定。',narration:'张角已病死。皇甫嵩闭营待敌松懈,鸡鸣突击广宗,斩张梁;次月又破张宝。',overlay:{heading:'鸡鸣破广宗',line:'起事席卷八州,主力却在同一年冬天接连瓦解。'},people:[{name:'皇甫嵩',office:'左中郎将、后拜左车骑将军',action:'待张梁军松懈后鸡鸣突击,并继续攻下曲阳'},{name:'张梁',office:'人公将军',action:'守广宗,战败被斩;不展示死亡'},{name:'张角、张宝',office:'天公将军、地公将军',action:'张角此前病死;张宝次月败于下曲阳'}],image:img('05-dawn-attack-at-guangzong','鸡鸣前的广宗外,皇甫嵩军悄然从闭营状态转为列队突击,东方微亮,黄巾守军刚开始仓促集合;张角之死只用远处熄灭坛火暗示,不展示尸体。','Before sunrise outside Guangzong, Huangfu Song, same identity, silently orders rested Han units from a closed camp into a sudden disciplined advance. The eastern horizon just begins to pale; Zhang Liang’s Yellow Turban defenders wake and assemble in visible haste beyond modest earthworks. An extinguished rural altar and covered empty command seat only suggest that Zhang Jue has already died of illness. Focus on timing and surprise, not slaughter; no body, no beheading, no burial desecration.','营垒、鸡鸣光线、敌军松懈状态与空席为视觉重构;闭营休士、潜夜勒兵、鸡鸣赴阵、张角先死和张梁败有史载。'),sources:['《后汉书·皇甫嵩朱儁列传》卷七十一','《后汉书·孝灵帝纪》卷八']}
],bgm:{catalogId:'zhanzhen:kunyang#2',title:'昆阳之战',version:'守城八千版·仅复用无主唱守城器乐 stem',status:'pending-export',pathOrUrl:'',usage:'删除昆阳人名、歌词和暴雨音效,保留紧张弦乐与稀疏军鼓;第三幕在风火处短促抬升,第五幕不做屠杀式高潮。',reason:'项目尚无黄巾专属曲;守城小卒视角可承接长社受围与突围,仓库暂无成品音频。',cues:[{from:'00:00',to:'00:18',scene:'01',direction:'分散脉冲表现八州响应。'},{from:'00:18',to:'00:36',scene:'02',direction:'压低声场。'},{from:'00:36',to:'00:54',scene:'03',direction:'风火处短促增强。'},{from:'00:54',to:'01:12',scene:'04',direction:'援军抵达后形成合奏。'},{from:'01:12',to:'01:28',scene:'05',direction:'鸡鸣突击后冷静收束。'}]},sources:[{claim:'三十六方、八州响应、长社火攻、曹操援军与广宗下曲阳诸战',citation:'《后汉书·皇甫嵩朱儁列传》卷七十一',url:'https://zh.wikisource.org/zh-hans/后汉书/卷71'},{claim:'黄巾同日反叛、朝廷部署及当年诸战月序',citation:'《后汉书·孝灵帝纪》卷八',url:'https://zh.wikisource.org/zh-hans/后汉书/卷08'}],blacklist:['把黄巾三十六万画成单一战场上的精确兵力或统一正规军','把黄色头巾误画成后世红巾,或让所有人穿鲜黄制服','把长社大胜归为曹操一人;主谋与主战者是皇甫嵩,朱儁及曹操合兵','加入《三国演义》虚构单挑、法术、妖术或神迹','表现张角戮尸、张梁张宝斩首、京观或大量死亡的血腥画面','现代文字地图、水印、后世服饰建筑、火器爆炸或动漫画风']},
{dir:'官渡之战(乌巢劫粮)',event:{canonicalName:'官渡之战(乌巢劫粮)',aliases:['官渡之战','乌巢之战','曹操破袁绍'],era:'东汉献帝建安五年',date:'公元200年二月至十月',place:'白马、延津、官渡与乌巢(今河南北部一带)',summary:'袁绍据四州率十余万众南下,曹操先解白马围、后在官渡相拒;许攸来奔后曹操亲率五千夜袭乌巢,袁军粮屯被破,张郃、高览来降,袁绍父子北渡。'},narration:{language:'zh-CN',estimatedSeconds:87,fullText:'建安五年,袁绍兼有四州,率十余万众南下攻许。二月,颜良围白马,荀攸建议先佯作渡河袭袁军后方,再用轻兵急趋白马。袁绍果然分兵西应,曹操转兵解围,颜良被关羽斩杀。双方随后在官渡相拒。袁军修土山、挖地道,曹军以投石车和营内壕沟应对;但曹操兵少粮尽,军心日益疲惫。十月,袁绍谋臣许攸离营来投,告诉曹操淳于琼带着万余人守护乌巢粮屯,离袁军大营约四十里。曹操留下曹洪守营,亲率步骑五千夜行,天亮前到达乌巢,放火攻屯。袁绍没有集中全军救粮,反而派张郃强攻曹操本营。乌巢被破,淳于琼败死,张郃、高览得知后转而投降。袁军随即崩溃,袁绍和袁谭弃军北渡黄河。官渡并非单靠一把火取胜,它还取决于白马解围、数月坚守,以及袁绍在粮屯与本营之间作出的错误选择。'},scenes:[
{id:'01',title:'声西击白马',timePlace:'二月,延津至白马',purpose:'用荀攸声东击西方案交代官渡前哨战。',narration:'荀攸建议佯渡延津,诱袁绍分兵,再以轻兵急趋白马。曹军解围,颜良被斩。',overlay:{heading:'声西击白马',line:'先让袁绍看向西边,再在白马突然出现。'},people:[{name:'荀攸',office:'曹操军师',action:'提出佯渡延津、轻兵袭白马之策'},{name:'曹操',office:'司空、曹军主帅',action:'采纳计策并亲自救白马'},{name:'关羽、颜良',office:'曹军偏将、袁军将领',action:'关羽在白马阵中斩颜良;不表现斩首近景'}],image:img('01-feint-at-yanjin-strike-baima','黄河延津一带,曹军少量舟具佯作渡河吸引袁军西移;另一侧曹操、荀攸看着轻兵沿堤道急趋白马,关羽只作远处骑将,不画单挑。','Wide Yellow River plain near Yanjin. A small visible Cao detachment and simple ferry craft stage preparations to cross, drawing distant Yuan columns west. In the foreground Cao Cao, an East Asian commander in his mid-40s with compact build, sharp eyes, short moustache and practical dark-red-brown lamellar armor, confers with the composed strategist Xun You in plain scholar-official robes while light troops hurry along a separate embankment toward Baima. Guan Yu appears only as a distant tall mounted officer with long beard and period-appropriate dark armor, no green opera costume and no duel close-up.','渡河佯动、视线层次、行军路径和人物外貌为视觉重构;荀攸声东击西、轻兵袭白马及解围有史载。'),sources:['《三国志·魏书·武帝纪》卷一']},
{id:'02',title:'相拒而粮尽',timePlace:'八月至十月,官渡营垒',purpose:'表现数月攻守和曹军粮乏,而非直接跳到乌巢。',narration:'两军相拒数月。袁军筑土山、挖地道,曹军以发石车和营内壕沟应对,粮食却将耗尽。',overlay:{heading:'相拒而粮尽',line:'营垒守住了,时间却在替袁绍消耗曹操。'},people:[{name:'曹操',office:'曹军主帅',action:'坚守官渡并处理粮尽困局'},{name:'袁绍',office:'袁军主帅',action:'以土山、地道与优势兵力持续进攻'},{name:'曹军士卒与民夫',office:'守军及运输人员',action:'防御土山地道并维持不足的补给'}],image:img('02-stalemate-and-empty-granary','官渡曹营内,士卒填堵袁军地道、操纵早期机械抛石器,远处袁军土山弓弩压营;前景粮仓近空,曹操检查最后几袋谷物。','Inside Cao Cao’s fortified camp at Guandu. Soldiers shore up a counter-trench against Yuan tunneling and operate a plausible tractionless lever stone-throwing device described as a thunder cart, kept mechanically simple; beyond the palisade Yuan archers shoot from newly raised earth mounds. In the foreground an almost empty grain shed holds only a few sacks as Cao Cao checks the ration tally with exhausted quartermasters. Human-scale engineering, no trebuchet fantasy, no readable tally.','器械外观、土山距离、粮袋数量与曹操巡仓为视觉重构;袁军土山地道、曹军发石车凿长堑及粮少有史载。'),sources:['《三国志·魏书·武帝纪》卷一']},
{id:'03',title:'许攸夜奔',timePlace:'十月,官渡曹营夜门',purpose:'呈现情报转折,不采用跣足迎接等缺乏正文支撑的名场面。',narration:'许攸离开袁营,夜投曹操,指出乌巢粮屯守备可袭。曹操决定亲自出兵。',overlay:{heading:'许攸夜奔',line:'四十里的距离,把一条情报变成了整场战争的赌注。'},people:[{name:'许攸',office:'袁绍谋臣、来奔者',action:'向曹操透露乌巢粮屯与守将信息'},{name:'曹操',office:'曹军主帅',action:'采纳袭乌巢建议并决定亲率五千出营'}],image:img('03-xu-you-arrives-at-night','官渡曹营夜门,风尘仆仆的许攸独自下马接受检查,曹操在小型军帐内听他指向无字木牍与粮屯方向,曹洪和少数军吏在旁,不画跣足夸张迎接。','Night at the guarded gate of Cao Cao’s Guandu camp. Xu You, a travel-worn East Asian scholar-official in his late 40s with dusty robe and anxious expression, dismounts alone and is carefully searched before entry. In a small lamplit command shelter he points toward blank wooden route slips and the unseen direction of Wuchao while Cao Cao listens intensely; Cao Hong and a few officers wait. Secrecy and verification, no theatrical barefoot greeting, no readable map.','抵营流程、人物外貌、曹洪在场与无字木牍为视觉重构;许攸来奔、说曹操击淳于琼等有史载。'),sources:['《三国志·魏书·武帝纪》卷一','《三国志·魏书·董二袁刘传》卷六']},
{id:'04',title:'五千夜袭乌巢',timePlace:'十月夜至黎明,乌巢',purpose:'表现曹操亲率五千、伪装通行与火攻粮屯。',narration:'曹操留曹洪守营,亲率步骑五千夜行,天明抵达乌巢,纵火攻粮。',overlay:{heading:'五千夜袭',line:'曹操把本营留在身后,把胜负押在天亮以前。'},people:[{name:'曹操',office:'曹军主帅',action:'亲率五千夜袭乌巢'},{name:'淳于琼',office:'袁军宿将、乌巢守将',action:'率万余人守粮屯,受袭战败'},{name:'曹洪',office:'曹军将领',action:'留守官渡本营'}],image:img('04-night-raid-on-wuchao','黎明前的乌巢粮屯,曹操率小队从木栅缺口突入,多处粮垛和车棚起火但无爆炸;袁军仓促结阵,曹操不作持火把单骑英雄。','Before dawn at Wuchao’s large but practical grain depot. Cao Cao, matching earlier scenes, leads disciplined infantry and riders through breached timber fencing as separate grain stacks and cart sheds ignite with controlled flames—no gunpowder blast. Chunyu Qiong’s surprised defenders form up among storehouses while Cao’s troops fight inward under shields. Convey a five-thousand-man raid through depth and multiple lanes, never one superhero with a torch; no burning people.','粮屯布局、突入方向、燃点与守军集结为视觉重构;曹操自将步骑五千夜往、会明至、放火与殊死战有史载。'),sources:['《三国志·魏书·武帝纪》卷一']},
{id:'05',title:'粮破军溃',timePlace:'乌巢失守后,官渡与黄河退路',purpose:'以张郃高览来降、袁绍父子北渡收束,不展示坑杀。',narration:'乌巢失守,张郃、高览来降。袁军崩溃,袁绍父子弃军北渡,河北门户由此动摇。',overlay:{heading:'粮破军溃',line:'十余万人的阵线,先从一座粮屯失去支点。'},people:[{name:'张郃、高览',office:'袁军将领',action:'闻乌巢破后率部来降'},{name:'袁绍、袁谭',office:'袁军主帅及其子',action:'弃军北渡黄河'},{name:'曹操',office:'曹军主帅',action:'受降并收取袁军辎重图书'}],image:img('05-yuan-army-collapses','清晨官渡营前,张郃高览放下兵符与佩剑率部列队受降,远处袁绍父子乘小船北渡黄河,袁军辎重车被完整接收,不展示坑杀或尸山。','Morning after Wuchao. Outside the Guandu camp, Zhang He and Gao Lan, two East Asian Yuan commanders in worn armor, place command tallies and sheathed swords on a low table as their units stand in ordered surrender. Far across the deep background, Yuan Shao and Yuan Tan cross the Yellow River in modest boats with a small escort. Cao troops take custody of intact baggage carts, documents and grain gear. Restrained strategic collapse, no kneeling spectacle, no massacre, no execution or body piles.','受降礼节、黄河同框、渡船和辎重位置为视觉重构;郃等来降、袁绍父子弃军渡河与尽收辎重有史载。'),sources:['《三国志·魏书·武帝纪》卷一']}
],bgm:{catalogId:'mingchangmian:guandu_wuchao#0',title:'官渡之战·火烧乌巢',version:'电影级配乐·建议使用无主唱器乐 stem',status:'pending-export',pathOrUrl:'',usage:'第一二幕保持缺粮压迫,第三幕压低至耳语式音色,第四幕为唯一火攻高潮,第五幕不做屠戮收尾;删除歌词与品牌尾签。',reason:'项目内有乌巢转折专属自生成音乐,结构与五幕完全对应,仓库暂无成品音频。',cues:[{from:'00:00',to:'00:18',scene:'01',direction:'河面与快速转兵节拍。'},{from:'00:18',to:'00:36',scene:'02',direction:'持续低频表现相持。'},{from:'00:36',to:'00:54',scene:'03',direction:'收窄到单音与低弦。'},{from:'00:54',to:'01:12',scene:'04',direction:'乌巢火起形成高潮。'},{from:'01:12',to:'01:27',scene:'05',direction:'军溃后迅速收束。'}]},sources:[{claim:'白马声东击西、官渡相拒、许攸来奔、曹操五千夜袭乌巢及袁绍北渡',citation:'《三国志·魏书·武帝纪》卷一',url:'https://zh.wikisource.org/zh-hans/三国志/卷01'},{claim:'乌巢屯粮、张郃高览来降与袁军决策',citation:'《三国志·魏书·董二袁刘传》卷六',url:'https://zh.wikisource.org/zh-hans/三国志/卷06'}],blacklist:['把官渡简化成曹操直接烧粮一幕,省略白马解围与数月相拒','采用许攸跣足出迎、关羽单骑斩将等戏剧化近景而不标明出处层级','把发石车画成后世配重投石机,或把地道画成现代矿井','展示淳于琼受刑、割鼻、袁军坑杀或八万尸体等血腥画面','虚构曹操与袁绍阵前会面、单挑或现代地图箭头','现代文字、水印、后世服饰建筑、火器爆炸或动漫画风']},
{dir:'赤壁之战(黄盖火攻)',event:{canonicalName:'赤壁之战(黄盖火攻)',aliases:['赤壁之战','乌林之战','黄盖诈降火攻'],era:'东汉献帝建安十三年',date:'公元208年秋冬',place:'长江中游赤壁、乌林一带(古战场具体位置存在讨论)',summary:'曹操取得荆州水军后沿江东下,孙权遣周瑜、程普与刘备并力拒曹;曹军已有疾病且初战不利,黄盖以诈降火船烧毁相接船舰并延烧岸营,曹操退走。'},narration:{language:'zh-CN',estimatedSeconds:88,fullText:'建安十三年,曹操南下荆州,刘琮举众投降,曹军得到荆州水军和大批船只。曹操继续沿江东进,江东将士震恐。孙权最终决定拒战,派周瑜、程普率数万水军,与刘备合力迎敌。两军在赤壁初次交锋时,曹军中已经流行疾病,又不习惯水战,交战不利后退到江北乌林。为减少船上颠簸,曹军把部分船舰首尾相接。黄盖观察到曹军人多、联军人少,难以久持,便向周瑜提出火攻,并先写信向曹操诈降。他准备数十艘轻便战船,装满干草薪柴,灌入膏油,外覆帷幕,后面系上小船供人撤离。火船驶近曹营时,黄盖一面称降,一面同时点火。正逢风势猛烈,船如箭发,火焰迅速延烧相连船舰和岸上营落。周瑜等率轻锐随后雷鼓大进,北军大败,曹操退回南郡方向。正史中的关键只有疾病、初战、相接船舰、黄盖诈降与火攻;借东风、草船借箭和苦肉计都不属于这场真实战役。'},scenes:[
{id:'01',title:'新得荆州舟师',timePlace:'建安十三年秋,荆州江面',purpose:'交代刘琮降与曹操快速接收一支尚未磨合的水军。',narration:'刘琮举众投降,曹操取得荆州水军和舟船,继续沿江东下。江东因此震恐。',overlay:{heading:'新得荆州舟师',line:'船变多了,真正熟悉这些船的人却未必已经服从。'},people:[{name:'曹操',office:'汉丞相',action:'接收荆州军并以舟师东进'},{name:'刘琮',office:'荆州牧刘表之子',action:'举众投降曹操;本幕只表现交接'},{name:'荆州水军',office:'新降部队',action:'并入曹军舟师,磨合与忠诚状态不作过度断言'}],image:img('01-cao-cao-receives-jingzhou-fleet','秋江码头,刘琮一方交出封闭木牍与船册,曹操站在岸上接收排列密集但尺度真实的荆州战船,北方士卒与原水军分区装载。','Autumn river landing in Jingzhou. Liu Cong, a young East Asian provincial ruler in subdued official robes, formally hands sealed blank wooden registers to Cao Cao, now late 40s with sharp eyes, short moustache and dark red-brown lamellar armor under a plain black cloak. Behind them, a large but physically plausible fleet of low Eastern Han river war boats is being loaded by visibly separate groups of northern soldiers and surrendered Jingzhou crews. Practical docks, reeds and overcast water, tension in the handover, no triumphal palace.','交接礼节、船型数量、两类士卒分区与人物外貌为视觉重构;刘琮举众降、曹操得其水军船步兵有史载。'),sources:['《三国志·吴书·周瑜鲁肃吕蒙传》卷五十四','《后汉书·孝献帝纪》卷九']},
{id:'02',title:'孙刘并力拒曹',timePlace:'江东军议及联军出发',purpose:'呈现孙权决战、周瑜程普率军并与刘备会合。',narration:'孙权决定拒战,命周瑜、程普率水军,与刘备并力逆曹。',overlay:{heading:'孙刘并力',line:'不是一方独胜,而是两支力量在大江上临时合成一道防线。'},people:[{name:'孙权',office:'讨虏将军、会稽太守',action:'决定拒曹并遣周瑜、程普出兵'},{name:'周瑜、程普',office:'江东左右都督',action:'率数万水军迎敌'},{name:'刘备',office:'豫州牧、左将军',action:'与周瑜、程普并力拒曹'}],image:img('02-sun-liu-fleet-forms','宽阔江面上,周瑜程普的江东船队与刘备一方较小船队从两侧汇合,岸上孙权送军只作远景;各船旗帜无字,人物服饰避免戏曲脸谱。','Wide Yangtze waterway as two allied flotillas join: Zhou Yu, a poised East Asian commander in his early 30s wearing refined but practical dark bronze-red lamellar armor, and the older veteran Cheng Pu command the disciplined Jiangdong line; Liu Bei, a middle-aged East Asian leader in plain brown-black armor and cloak, arrives with a smaller contingent from another channel. Sun Quan remains a distant shore figure sending them out. Distinct forces forming one line, no opera costumes or readable flags.','会师水域、两军比例、人物外貌与孙权送军远景为视觉重构;权遣瑜程普与备并力逆曹有史载。'),sources:['《三国志·吴书·周瑜鲁肃吕蒙传》卷五十四','《三国志·蜀书·先主传》卷三十二']},
{id:'03',title:'初战退次江北',timePlace:'赤壁初战后,江北乌林',purpose:'把疾病、初战不利和船舰相接放在火攻之前。',narration:'曹军已有疾病,初次交战失利,退到江北。部分船舰首尾相接,岸营与船阵靠得很近。',overlay:{heading:'退次江北',line:'相接让船更稳,也让一条火路连了起来。'},people:[{name:'曹操',office:'曹军主帅',action:'初战失利后引军退次江北'},{name:'曹军舟师',office:'北军与荆州水军',action:'受疾病与水土影响;部分船舰首尾相接'}],image:img('03-cao-fleet-at-wulin','阴冷江北乌林,曹军病卒在岸上简陋医棚休息,船工用木梁绳索把部分战船首尾相接,曹操巡看水营,远处孙刘船队保持距离。','Cold overcast north bank at Wulin after the first clash. Sick Cao soldiers rest in simple open medical shelters while crews use rope and timber braces to connect some low river war boats bow-to-stern; other vessels remain separate, avoiding an all-fleet chain myth. Cao Cao, matching scene 01, inspects the cramped anchorage and nearby shore camp. Across the broad river the allied fleet keeps distance. Damp fatigue and logistical vulnerability, no mass dying or modern quarantine.','医棚、连接比例、营船距离与曹操巡营为视觉重构;军众已有疾病、初战败退、船舰首尾相接有史载。'),sources:['《三国志·吴书·周瑜鲁肃吕蒙传》卷五十四']},
{id:'04',title:'黄盖诈降发火',timePlace:'赤壁江面,风起之际',purpose:'严格按正史呈现蒙冲斗舰、薪草膏油、走舸和同时发火。',narration:'黄盖先书诈降,再以数十艘装满薪草膏油的船驶近。火船同时点燃,借风直扑曹营。',overlay:{heading:'诈降发火',line:'曹营等来的是降船,靠近后才发现船上装的全是风与火。'},people:[{name:'黄盖',office:'周瑜部将',action:'提出火攻、先书诈降并率火船逼近'},{name:'周瑜',office:'联军主将',action:'采纳黄盖方案并准备后续轻锐进击'},{name:'曹军水营',office:'受降一方',action:'观察降船驶近,未及时识别火攻'}],image:img('04-huang-gai-fire-ships','江面强风中,黄盖所在前船升起无字降帜,数十艘低矮蒙冲装着覆幕的薪草油料向曹军水营靠近,船尾系小型走舸;前几艘刚点火,无爆炸。','Strong river wind as Huang Gai, an older East Asian veteran in his late 50s with weathered face, short gray moustache and practical dark red armor, commands the lead false-surrender vessel. Several dozen low mengchong assault boats carry bundled reeds, dry wood and oil jars hidden under plain curtains; small escape skiffs are tied astern. Blank surrender streamers are visible without writing. The first vessels ignite simultaneously only after closing distance, flames low and wind-bent, absolutely no gunpowder explosion or people burning.','火船结构、黄盖外貌、走舸连接与点火时机为视觉重构;载薪草、灌膏油、裹帷幕、建牙旗、系走舸与同时发火有史载。'),sources:['《三国志·吴书·周瑜鲁肃吕蒙传》卷五十四']},
{id:'05',title:'烟炎张天北军退',timePlace:'乌林水营与岸上营落',purpose:'表现火势延烧和联军随后进击,以曹操退走收束。',narration:'风盛火猛,火船延烧相接船舰与岸营。周瑜轻锐随后进击,曹操退走。',overlay:{heading:'烟炎张天',line:'火烧的是船营,改变的却是天下继续统一还是走向三分。'},people:[{name:'周瑜、程普、刘备',office:'孙刘联军将领',action:'乘火势率轻锐与联军进击'},{name:'曹操',office:'曹军主帅',action:'北军大坏后退走,保南郡方向'},{name:'曹军士卒',office:'舟师与岸营兵员',action:'在火势与疾病中撤离;不展示烧溺死亡'}],image:img('05-northern-army-retreats','乌林江岸远景,燃烧船舰顺风延及几处岸营,周瑜所部战船保持安全距离后推进,曹操在岸上组织残军与马匹撤向内陆;水面不展示溺尸。','Wide Wulin riverbank during the climax. Wind carries fire from several connected Cao ships into a few shore encampments, smoke towering but not consuming the entire horizon. Zhou Yu’s allied boats advance from a safe upwind distance in disciplined formation, drums implied visually. On land Cao Cao, matching earlier scenes, organizes surviving soldiers, horses and carts into a retreat toward Nan commandery rather than fleeing alone. No burning people, drowned bodies, heroic duel or fantasy inferno.','火势范围、联军距离、曹操组织撤退与水陆同框为视觉重构;风盛、延烧岸营、轻锐雷鼓大进、北军大坏退走有史载。'),sources:['《三国志·吴书·周瑜鲁肃吕蒙传》卷五十四','《三国志·蜀书·先主传》卷三十二']}
],bgm:{catalogId:'zhanzhen:chibi#0',title:'赤壁之战',version:'周瑜版·电影级配乐·建议使用无主唱器乐 stem',status:'pending-export',pathOrUrl:'',usage:'前两幕维持江面张力,第三幕抽去高频表现病军与水土不服,第四幕风火形成唯一高潮,第五幕随曹军撤退降温;删除歌词与品牌尾签。',reason:'项目内赤壁专属自生成音乐,人物视角和火攻结构与分幕一致,仓库暂无成品音频。',cues:[{from:'00:00',to:'00:18',scene:'01',direction:'低弦托住舰队规模。'},{from:'00:18',to:'00:36',scene:'02',direction:'两支船队会合时拓宽声场。'},{from:'00:36',to:'00:54',scene:'03',direction:'抽去主鼓。'},{from:'00:54',to:'01:12',scene:'04',direction:'风火同时抬升。'},{from:'01:12',to:'01:28',scene:'05',direction:'撤退中迅速冷却。'}]},sources:[{claim:'曹操得荆州水军、孙刘并力、初战疾病、船舰相接、黄盖诈降火攻及北军败退',citation:'《三国志·吴书·周瑜鲁肃吕蒙传》卷五十四',url:'https://zh.wikisource.org/zh-hans/三国志/卷54'},{claim:'刘备与周瑜程普并力、大破曹军并焚舟船',citation:'《三国志·蜀书·先主传》卷三十二',url:'https://zh.wikisource.org/zh-hans/三国志/卷32'},{claim:'曹操舟师伐孙权、周瑜败之于乌林赤壁',citation:'《后汉书·孝献帝纪》卷九',url:'https://zh.wikisource.org/zh-hans/后汉书/卷09'}],blacklist:['加入借东风、草船借箭、诸葛亮设坛、周瑜打黄盖等《三国演义》情节','把全部曹军船舰画成铁链锁死;正史只说首尾相接,不载铁索范围','把曹军号称或综合兵力直接画成精确八十万密集人海','把黄盖画成受刑带伤或让曹操亲自在船头迎接','展示烧人、溺尸、水面尸体或伤亡特写','现代文字、水印、后世楼船服饰、火器爆炸或动漫画风']}
]
function md(p){const a=[`# ${p.event.canonicalName}|素材说明`,'','## 事件边界','',`- 规范事件名:${p.event.canonicalName}`,`- 常用称呼:${p.event.aliases.join('、')}`,`- 时代:${p.event.era}`,`- 日期:${p.event.date}`,`- 地点:${p.event.place}`,`- 一句话结论:${p.event.summary}`,'','## 60–90 秒主旁白','',p.narration.fullText,'','## 分幕素材',''];for(const s of p.scenes)a.push(`### ${s.id}|${s.title}`,'',`- 时间地点:${s.timePlace}`,`- 本幕作用:${s.purpose}`,`- 分幕旁白:${s.narration}`,`- 页面标题:${s.overlay.heading}`,`- 页面短句:${s.overlay.line}`,`- PNG:\`${s.image.original}\``,`- WebP:\`${s.image.delivery}\``,`- 替代文本:${s.image.alt}`,`- 视觉推演边界:${s.image.visualInference}`,`- 人物:${s.people.map(x=>`${x.name}(${x.office};${x.action})`).join(';')}`,`- 史料:${s.sources.join(';')}`,'','生成提示词:','','```text',s.image.prompt,'```','');a.push('## BGM 选曲','',`- 项目曲目:\`${p.bgm.catalogId}\`|${p.bgm.title}|${p.bgm.version}`,`- 状态:${p.bgm.status}`,`- 用法:${p.bgm.usage}`,`- 选择理由:${p.bgm.reason}`,'- 音频路径或 URL:仓库暂缺,待导出','','| 时间 | 分幕 | 编排 |','|---|---|---|',...p.bgm.cues.map(x=>`| ${x.from}–${x.to} | ${x.scene} | ${x.direction} |`),'','## 史料来源','',...p.sources.map(x=>`- ${x.claim}:[${x.citation}](${x.url})`),'','## 禁区与误读防护','',...p.blacklist.map(x=>`- ${x}`),'','## 交付检查','','- [ ] 每幕 PNG 原图存在且为 16:9','- [ ] 每幕 WebP 交付图存在且为 16:9','- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全','- [ ] BGM 已导出并填写可用路径','- [ ] \`manifest.json\` 与本说明中的文件名全部校验通过','');return a.join('\n')+'\n'}
for(const p of packs){const d=path.join(outRoot,p.dir);if(fs.existsSync(d))throw new Error(`Refusing to overwrite ${d}`);fs.mkdirSync(path.join(d,'images'),{recursive:true});fs.writeFileSync(path.join(d,'manifest.json'),JSON.stringify({event:p.event,narration:p.narration,scenes:p.scenes,bgm:p.bgm,sources:p.sources,blacklist:p.blacklist},null,2)+'\n');fs.writeFileSync(path.join(d,'素材说明.md'),md(p))}
console.log(`Generated ${packs.length} Eastern Han core battle packages`)
@@ -0,0 +1,86 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed fourth-to-sixth-century China, cinematic 16:9 landscape, subtle film grain, physically plausible armies and structures, rammed earth, aged timber, hemp, low-sheen silk, leather, iron lamellar armor, straight swords, bows, crossbows, spears, cavalry tack, wooden carts and period river craft. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Eastern Jin and Northern-Southern Dynasties story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const cue = (scene, from, to, direction) => ({ scene, from, to, direction })
const packs = [
{
dir: '东晋/淝水之战(北府兵渡水破秦)',
event: { canonicalName: '淝水之战(北府兵渡水破秦)', aliases: ['淝水之战', '肥水之战', '谢玄破苻坚'], projectDynasty: '东晋', era: '东晋孝武帝太元八年、前秦建元十九年', date: '公元383年十一月', place: '寿阳与淝水一线', summary: '前秦苻坚大军进至寿阳,东晋谢石、谢玄率北府兵迎战;晋军请秦军稍退以便渡水决战,秦军后撤时因朱序呼喊与指挥失控而大乱,晋军乘势渡河追击获胜。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '太元八年,前秦苻坚大举南下,先取寿阳,把主力压到淝水西岸。东晋朝廷并没有一支与秦军数量对等的大军,前线由谢石、谢玄等统领北府兵迎战。刘牢之先在洛涧击破秦军前锋,晋军推进到淝水东岸。两军隔水对峙,谢玄派人请求秦军稍向后退,让晋军渡水后决战。苻坚想等晋军半渡再击,命军队后撤;但庞大军阵一旦移动,命令很快失真。此前被秦军俘获、此时在阵中的朱序趁机高喊“秦兵败矣”,后部误以为前线真的战败,退却转成奔乱。谢玄、谢琰率军迅速渡水追击,秦将苻融试图整军,落马后战死,秦军全面溃退。苻坚中箭北走,沿途风声鹤唳、草木皆兵是《晋书》对败军惊惧的记述,不是神异。建康的谢安接到捷报时仍与客人下棋,只淡淡说“小儿辈遂已破贼”,回屋过门槛才折断木屐齿。这场胜利保住了东晋,也让前秦的统一迅速瓦解。' },
scenes: [
{ id: '01', title: '寿阳压境·洛涧先胜', timePlace: '寿阳东南、洛涧至淝水', purpose: '建立秦军压境与北府兵先击前锋的战场态势。', narration: '苻坚军占据寿阳。刘牢之率北府精兵先破洛涧前锋,晋军才得以进至淝水。', overlay: { heading: '先夺洛涧', line: '晋军先打掉渡口前锋,才把决战线推到淝水。' }, people: [{ name: '刘牢之', office: '鹰扬将军、广陵相', action: '率精兵击秦军洛涧前锋' }, { name: '梁成', office: '前秦卫军将军', action: '扼守洛涧,战败;不展示遗体' }, { name: '谢玄', office: '前锋都督', action: '统北府兵推进至淝水' }], image: image('01-liu-laozhi-wins-at-luojian', '秋冬清晨的洛涧渡口,刘牢之率深青灰北府兵用盾弩稳步夺取木桥与浅滩,秦军前锋向远处寿阳方向退却,不画尸体。', 'Cold dawn at a modest stream crossing near Luojian. Liu Laozhi, vigorous in his 30s, compact beard and dark blue-gray lamellar armor, leads disciplined Beifu shield and crossbow infantry across a timber bridge and shallow ford. Former Qin forward troops in brown-black armor disengage toward distant Shouyang earthworks. Show controlled tactical success and secured crossing, no slaughter, bodies or impossible army size.', '木桥、浅滩、兵种和退却方向为视觉重构;刘牢之击破洛涧前锋、晋军进至淝水有史载。'), sources: ['《晋书·谢玄传》卷七十九', '《资治通鉴》卷一百五'] },
{ id: '02', title: '隔水请退', timePlace: '淝水两岸', purpose: '呈现谢玄请秦军后撤和苻坚欲半渡而击的决策点。', narration: '谢玄请秦军稍退,让晋军渡水决战。苻坚想在半渡时进击,下令全军后撤。', overlay: { heading: '请君少却', line: '决定胜负的命令不是向前,而是让一支庞大军队先向后移动。' }, people: [{ name: '谢玄', office: '东晋前锋都督', action: '遣使请秦军退阵,许晋军渡水' }, { name: '苻坚', office: '前秦天王', action: '同意退军,准备半渡而击' }, { name: '苻融', office: '前秦征南大将军', action: '在前线传达整军后退命令' }], image: image('02-request-to-withdraw-at-fei-river', '淝水宽阔河岸,晋使乘小舟在中流递交无字木牍;西岸苻坚苻融在高处观察纵深极大的秦军阵列,东岸谢玄北府兵整齐等待。', 'Across a broad shallow Fei River, an Eastern Jin envoy in a small boat raises a sealed blank wooden dispatch. On the west bank Fu Jian, late 40s, imposing but controlled, and Fu Rong survey a deep Former Qin formation stretching behind them; on the east bank Xie Xuan’s smaller Beifu ranks wait in compact order. No movement yet. Emphasize scale mismatch and the river as negotiated space, no banners with writing.', '使者方式、阵列规模、两岸站位和地形为视觉重构;谢玄请却军、苻坚欲半渡击而许之有史载。'), sources: ['《晋书·苻坚载记》卷一百十四', '《晋书·谢玄传》卷七十九'] },
{ id: '03', title: '秦阵后移·朱序呼败', timePlace: '淝水西岸秦军阵中', purpose: '解释有序后撤如何转成无法停止的奔乱。', narration: '秦军依令后移,阵列逐层挤压。朱序高喊秦军已败,后部误信,退却变成溃乱。', overlay: { heading: '一退不复止', line: '前线只退几步,传到数里之后,就成了全军都在败走。' }, people: [{ name: '朱序', office: '前秦度支尚书、原东晋降将', action: '在秦军后撤时高喊秦兵已败' }, { name: '苻融', office: '前秦前线主帅', action: '试图驰骑整顿后撤军阵' }, { name: '前秦军士', office: '多部军阵', action: '因命令失真与拥挤由退转乱' }], image: image('03-zhu-xu-shouts-as-qin-retreats', '秦军纵深阵列正在后移,前排尚举盾有序,后排因车辆与人群挤压转身奔走;朱序站在一辆土台车旁高声呼喊,苻融骑马逆流整队。', 'A deep Former Qin army begins a commanded withdrawal: front ranks still face the river with shields up, middle ranks step backward, while rear units turn amid carts and dust as spacing collapses. Zhu Xu, a middle-aged unarmored official among the formation, shouts from beside a low command wagon. Fu Rong rides against the human flow trying to restore order. No supernatural panic, trampling close-up, bodies or gore.', '阵列失真过程、朱序位置、车队拥堵与苻融整队为战术推演;秦军一退不止、朱序呼秦兵败矣有史载。'), sources: ['《晋书·苻坚载记》卷一百十四', '《资治通鉴》卷一百五'] },
{ id: '04', title: '北府渡水·谢安折屐', timePlace: '淝水战场与建康谢安宅的蒙太奇式同框', purpose: '以渡水追击和后方接报构成战役收束。', narration: '谢玄率北府兵渡水追击,秦军溃败。建康谢安接报仍下棋,入室时却折断木屐齿。', overlay: { heading: '小儿辈破贼', line: '前线越过一条河,后方只落下一枚棋子;压住的喜悦却折在门槛上。' }, people: [{ name: '谢玄、谢琰', office: '北府军将领', action: '率军渡水追击溃退秦军' }, { name: '谢安', office: '征讨大都督、东晋执政', action: '在建康下棋接报,入户时折屐齿' }, { name: '苻坚', office: '前秦天王', action: '败退北走;不展示伤口' }], image: image('04-beifu-crosses-xie-an-receives-news', '主体为北府兵用浅滩与小舟渡过淝水、盾弩整齐推进;画面右侧以室内开窗式构图呈现谢安在棋盘旁读捷报,门槛边木屐齿折断,无超现实分屏文字。', 'The main frame shows Beifu infantry crossing the Fei River by shallow ford and small boats in disciplined shield-and-crossbow groups as the larger Qin army recedes into dust. Architecturally framed at the right edge, a quiet riverside command pavilion evokes Jianye: elderly Xie An sits by a go board reading a blank dispatch, while a broken wooden clog tooth lies at the threshold. A cinematic visual parallel rather than a supernatural split screen.', '两地同框、渡河方式、棋局与折屐细节为视觉化并置;晋军渡水追击、谢安围棋接报和折屐齿有史载。'), sources: ['《晋书·谢安传》《谢玄传》卷七十九', '《资治通鉴》卷一百五'] },
],
bgm: { catalogId: 'zhanzhen:feishui#1', title: '淝水之战', version: '谢玄版·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '洛涧以克制快板起,隔水请退压住节奏,朱序呼喊处让拍点错位,渡水追击形成主峰,尾声骤收至棋子和木屐声;删除歌词和品牌尾签。', reason: '项目有淝水专属谢玄版,结构核心正是克制、后退与渡水爆发;仓库暂无成品音频。', cues: [cue('01','00:00','00:20','北府先锋建立节拍。'),cue('02','00:20','00:40','隔水对峙压低配器。'),cue('03','00:40','01:02','军阵后移后节奏错位。'),cue('04','01:02','01:26','渡水抬升,棋子与折屐骤收。')] },
sources: [{ claim: '谢安统筹、谢玄北府兵作战与接报折屐', citation: '《晋书·谢安传、谢玄传》卷七十九', url: 'https://zh.wikisource.org/zh-hans/晋书/卷079' }, { claim: '苻坚临淝水、退军失控与风声鹤唳', citation: '《晋书·苻坚载记下》卷一百十四', url: 'https://zh.wikisource.org/zh-hans/晋书/卷114' }, { claim: '洛涧、请退、朱序呼败与战后次序', citation: '《资治通鉴》卷一百五', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷105' }],
blacklist: ['把“八公山草木皆兵”画成树木变士兵或神迹', '使用八十万、百万等数字生成不可验证的人海尸山', '把谢安画在淝水前线指挥;他在建康统筹', '虚构苻坚与谢玄单挑或朱序持刀冲阵', '现代文字、水印、后世服甲建筑、火器或血腥画面'],
},
{
dir: '梁/台城之战(侯景攻陷建康)',
event: { canonicalName: '台城之战(侯景攻陷建康)', aliases: ['台城之战', '侯景之乱·台城陷落', '建康之围'], projectDynasty: '梁', era: '南梁武帝太清二年至三年', date: '公元548年八月至549年三月', place: '寿阳、采石、建康与台城', summary: '降梁不久的侯景在寿阳起兵,经采石渡江直趋建康,在萧正德内应下进入外郭并长期围攻台城;援军互相观望,台城粮尽后陷落,梁武帝被控制并于净居殿去世。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '太清二年,东魏降将侯景据寿阳反梁。他起兵时力量并不占绝对优势,却准确判断建康外强中空。侯景从采石一带渡过长江,沿江疾进;临贺王萧正德暗中接应,使叛军得以进入建康外郭。梁武帝退守宫城台城,命将守住城门与宫墙。台城不是一夜陷落:围攻从秋冬拖到次年春,城内粮食和水源日益紧张,疫病与饥饿削弱守军。各地勤王军陆续抵达城外,却因统属、猜忌和观望没有形成统一攻击,反而让侯景得以分兵拒守。太清三年三月,台城终于被攻破。侯景入见梁武帝,仍以臣礼自称,但皇帝已失去军政自由。五月,梁武帝死于净居殿;史书留下“荷荷”二字作为他临终索蜜未得的声音,但细节不应扩写成戏剧对白。台城陷落不是单纯的勇将破城,而是内应、空虚的首都防务、长期围困与援军失去协同共同造成。侯景之乱随后继续撕裂江南,直到552年才被平定。' },
scenes: [
{ id: '01', title: '寿阳举兵·采石渡江', timePlace: '寿阳至采石江面', purpose: '建立侯景以快速渡江直取首都的路线。', narration: '侯景从寿阳举兵,沿江疾进,在采石一带组织舟筏渡过长江。', overlay: { heading: '直趋建康', line: '叛军不在淮南久战,而是把速度换成首都来不及准备的时间。' }, people: [{ name: '侯景', office: '河南王、梁司州刺史,旋即叛乱', action: '自寿阳起兵并率军渡江' }, { name: '梁沿江守军', office: '江防部队', action: '仓促监视渡江,未能形成完整拦截' }], image: image('01-hou-jing-crosses-at-caishi', '秋日长江采石江面,侯景骑在岸边督促步骑登上浅底木船与竹木筏,少量梁军烽火在远岸示警,舟船朴素可信。', 'Autumn at the broad Yangtze near Caishi. Hou Jing, around 45, compact and hard-faced with close beard, mixed northern-style dark lamellar armor, directs infantry and horses onto shallow timber boats and lashed rafts. Small Liang beacon posts signal from the distant south bank but no grand fleet blocks the passage. Urgent logistics and current, no fantastical armada, burning city or mass battle.', '渡江舟具、兵马登船、江防距离与人物外貌为视觉重构;侯景自寿阳反、由采石渡江趋建康有史载。'), sources: ['《梁书·武帝纪下》卷三', '《资治通鉴》卷一百六十二'] },
{ id: '02', title: '内应开门·外郭失守', timePlace: '建康外郭与宣阳门一线', purpose: '说明叛军得以迅速进入首都的内部因素。', narration: '萧正德在城内接应。侯景军进入建康外郭,梁武帝只能退守台城。', overlay: { heading: '门从里面开', line: '首都最先失守的不是宫墙,而是被内应拆掉的防线。' }, people: [{ name: '萧正德', office: '临贺王', action: '与侯景勾连并在建康内接应' }, { name: '侯景', office: '叛军首领', action: '率军进入外郭、围逼台城' }, { name: '梁武帝萧衍', office: '皇帝', action: '收缩宫城守备' }], image: image('02-inside-gate-opens', '夜色建康外郭,一扇大型木门由城内士兵转动绞盘打开,侯景前队持盾安静通过;远处台城宫墙点灯戒备,无夸张攻城。', 'Night at a timber-and-rammed-earth outer gate of Jiankang. From inside, troops loyal to Xiao Zhengde turn a capstan and withdraw the gate bar while Hou Jing’s shielded vanguard passes in compact silence. Farther inside, the more substantial Taicheng palace walls light watch fires and close their own gates. The betrayal is spatially clear without labels, cheering crowds or fantasy architecture.', '城门机构、内应人数、侯景军进城次序与台城距离为视觉重构;萧正德内应、侯景入外郭围台城有史载。'), sources: ['《梁书·武帝纪下》卷三', '《南史·梁武帝纪》卷七'] },
{ id: '03', title: '台城久围·援军观望', timePlace: '台城内外,太清二年冬至三年春', purpose: '同时呈现城内困乏与城外勤王军缺乏统一。', narration: '围城数月,台城粮水渐尽。多路勤王军聚在城外,却互相观望,未能合力解围。', overlay: { heading: '援军在城外', line: '台城看得见营火,却等不到一支真正统一的救兵。' }, people: [{ name: '羊侃', office: '都官尚书、台城守将', action: '组织守军修门、分配物资' }, { name: '柳仲礼等', office: '勤王诸军将领', action: '各驻营垒,缺乏统一进攻' }, { name: '侯景', office: '围城主帅', action: '分兵围城并拒援' }], image: image('03-taicheng-besieged-relief-camps-watch', '冬日台城城墙上守军提水修补,空粮筐靠墙;城外侯景围营形成近圈,更远处多路勤王营火彼此分散,无尸体饥民特写。', 'Winter around Taicheng. On the palace wall exhausted defenders haul water and repair timber hoardings beside nearly empty grain baskets. Hou Jing’s siege lines form a close but imperfect ring. Beyond them, several separate relief camps with different blank standards and watchfires remain visibly uncoordinated. Commander Yang Kan oversees repairs. Show months of attrition and failed cooperation, no starving-body spectacle or giant siege engines.', '围营密度、援军营地分散、城内物资状态为视觉推演;台城久围、城内粮乏、援军不相统一有史载。'), sources: ['《梁书·武帝纪下》卷三', '《资治通鉴》卷一百六十二'] },
{ id: '04', title: '台城陷落·净居殿终局', timePlace: '台城与净居殿,太清三年三月至五月', purpose: '以被控制的宫门和空蜜盏收束,不重演死亡。', narration: '三月台城陷落,侯景入宫控制梁武帝。五月,皇帝死于净居殿,梁朝秩序彻底断裂。', overlay: { heading: '台城陷', line: '城门打开以后,八十六岁的皇帝还坐在殿里,天下却已不再听命于他。' }, people: [{ name: '梁武帝萧衍', office: '南梁皇帝', action: '被控制于宫中,后死于净居殿;不展示遗体' }, { name: '侯景', office: '叛军首领', action: '入见并掌控台城' }, { name: '宫人', office: '净居殿侍从', action: '守在空食案旁,具体临终对白不扩写' }], image: image('04-taicheng-falls-empty-honey-bowl', '破损台城宫门后,侯景在殿阶下保持名义臣礼,年迈萧衍隔着屏风坐于昏暗净居殿;前景食案只有空小碗,不画病亡身体。', 'After Taicheng’s damaged gate opens, Hou Jing stands below the hall steps in outwardly formal submission while armed guards quietly control every passage. Deep inside the dim Jingju Hall, the 86-year-old Emperor Wu sits behind a plain screen, upright but isolated. In the foreground a small food tray holds an empty honey bowl as restrained symbolic detail. No deathbed body, starvation close-up, kneeling humiliation or lavish palace.', '两人距离、守卫控制、净居殿陈设与空蜜碗为视觉转译;台城陷、侯景入见、梁武帝后死净居殿并有索蜜记载。'), sources: ['《梁书·武帝纪下》卷三', '《南史·梁武帝纪》卷七'] },
],
bgm: { catalogId: 'mingchangmian:taichengzhixian#0', title: '台城之陷·净居殿荷荷', version: '电影级配乐·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '渡江以压迫低频起,开门处不做英雄爆点;久围阶段持续削薄配器,台城陷落后只留空殿回响和碗盏声;删除歌词与品牌尾签。', reason: '项目有台城陷落专属曲,叙事视角和本包终局完全一致;仓库暂无成品音频。', cues: [cue('01','00:00','00:21','舟筏与水声低频推进。'),cue('02','00:21','00:42','开门时节奏短促收紧。'),cue('03','00:42','01:04','围城逐层削薄。'),cue('04','01:04','01:26','空殿回响与碗盏冷收。')] },
sources: [{ claim: '侯景反、建康围城、台城陷与梁武帝之死', citation: '《梁书·武帝纪下》卷三', url: 'https://zh.wikisource.org/zh-hans/梁书/卷003' }, { claim: '梁武帝晚年与台城终局', citation: '《南史·梁武帝纪》卷七', url: 'https://zh.wikisource.org/zh-hans/南史/卷007' }, { claim: '渡江、内应、围城与援军相持的日序', citation: '《资治通鉴》卷一百六十二', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷162' }],
blacklist: ['把侯景之乱画成北方族群对江南人的种族战争', '将萧正德内应省略成侯景正面一夜攻破全城', '把勤王军画成已统一决战并被一次击溃', '扩写“荷荷”为完整临终对白或猎奇饥饿死亡', '现代文字、水印、后世宫殿服甲、火器、尸堆与血腥'],
},
{
dir: '北齐/邙山之战(兰陵王免胄解围)',
event: { canonicalName: '邙山之战(兰陵王免胄解围)', aliases: ['邙山之战', '洛阳之战', '兰陵王入阵'], projectDynasty: '北齐/北周', era: '北齐河清三年、北周保定四年', date: '公元564年冬', place: '洛阳、金墉城与邙山', summary: '北周联合突厥攻北齐,围困洛阳金墉城;北齐段韶、斛律光、高长恭率援军至邙山,高长恭率五百骑突入围阵,到城下脱去头盔示面获守军辨认,城上弩手接应,齐军遂破周军。' },
narration: { language: 'zh-CN', estimatedSeconds: 82, fullText: '河清三年,北周联合突厥大举进攻北齐,周军主力推进到洛阳,围住金墉城。北齐派段韶、斛律光和兰陵王高长恭率军救援。援军抵达邙山时,金墉守军已经被围多日,城内无法确认城外来军身份。北齐军先在山地展开,与北周军争夺通往洛阳的道路。高长恭率五百骑深入周军围阵,一直冲到金墉城下。城上守军怀疑来者是敌军诱骗,不敢立即接应;高长恭便脱去头盔,露出面容。守军认出兰陵王,才放下弩手火力支援。齐军内外呼应,周军围阵动摇,最终撤退,北齐取得邙山大捷。正史写的是“免胄示之面”,没有说他戴着狰狞面具;后世《兰陵王入阵曲》与面具传说,是战后文化记忆,不应倒灌成战场实物。此役让高长恭声名大振,却没有改变北齐内部的政治危险;九年后,他仍因受到猜忌被赐死。' },
scenes: [
{ id: '01', title: '金墉被围', timePlace: '洛阳金墉城与邙山北麓', purpose: '建立守城孤立和周军围阵。', narration: '北周军压到洛阳,围住金墉城。城内守军只能等待北齐援军。', overlay: { heading: '洛阳告急', line: '山下是围军,城外的每一支来兵都可能是援军,也可能是诱敌。' }, people: [{ name: '北周围城军', office: '尉迟迥等所部', action: '在金墉城外构筑围阵' }, { name: '金墉守军', office: '北齐洛阳守军', action: '闭城守备并向邙山方向观察' }], image: image('01-jinyong-city-under-siege', '冬日洛阳金墉夯土城被多层北周营垒围困,城上弩手守望,远处邙山道路空旷,攻守双方旗面无字。', 'Winter at compact Jinyong City near Luoyang: sturdy rammed-earth walls and timber towers are encircled by layered Northern Zhou field camps, trenches and shield lines, not a fantasy metropolis. Northern Qi crossbowmen watch from the parapet toward an empty road descending from Mangshan. Smoke from cooking fires signals a prolonged siege. No active massacre, giant trebuchets or readable flags.', '金墉城形制、围营层次、视线方向和冬季环境为视觉重构;周军围洛阳金墉、齐军待援有史载。'), sources: ['《北齐书·兰陵武王传》卷十一', '《周书·武帝纪上》卷五'] },
{ id: '02', title: '段韶列阵·邙山争路', timePlace: '邙山南北坡与洛阳外', purpose: '表现援军先建立正面战线,为突骑开出通道。', narration: '段韶、斛律光率援军抵达邙山,与北周军争夺通往金墉的道路。', overlay: { heading: '援军到邙山', line: '五百骑能突入围阵之前,先要有一支大军把正面钉在山下。' }, people: [{ name: '段韶', office: '北齐太师', action: '统筹援军阵列与进攻方向' }, { name: '斛律光', office: '北齐司徒', action: '率部配合正面作战' }, { name: '高长恭', office: '兰陵王', action: '等待率精骑突入的机会' }], image: image('02-qi-relief-army-on-mangshan', '邙山冬季坡地,段韶与斛律光在高处观察北周盾阵,北齐步骑分层列队,高长恭带五百骑在侧后方收拢,人数不夸张。', 'On winter slopes of Mangshan, veteran Duan Shao and Hülü Guang study Northern Zhou shield formations below while Northern Qi infantry and cavalry deploy in terraced lines. Gao Changgong, late 20s, handsome but battle-worn, dark teal lamellar armor and ordinary practical helmet, gathers a compact elite cavalry group on the flank. Emphasize the larger relief battle enabling his penetration, no mask, no solo hero pose.', '援军阵列、坡地层次、精骑位置与三将外貌为视觉重构;段韶等率军至邙山、高长恭参与解围有史载。'), sources: ['《北齐书·段韶传》卷十六', '《北齐书·兰陵武王传》卷十一'] },
{ id: '03', title: '五百骑突阵·城下示面', timePlace: '金墉城下北周围阵', purpose: '还原免胄辨认,不使用面具传说。', narration: '高长恭率五百骑突入围阵。城上不辨敌我,他在城下脱去头盔示面。', overlay: { heading: '免胄示面', line: '他脱下的是头盔,不是传说中的面具;守军看见脸,才确认援军到了。' }, people: [{ name: '高长恭', office: '兰陵王', action: '率五百骑突至城下并免胄示面' }, { name: '金墉弩手', office: '北齐守军', action: '辨认兰陵王后开弩接应' }, { name: '北周围军', office: '围城部队', action: '转身应对突入围阵的骑兵' }], image: image('03-gao-changgong-removes-helmet', '金墉城下,高长恭与少量骑兵停在护城沟外,他抬手取下普通铁盔露出面容;城上弩手由戒备转向城外周军射击,不画面具。', 'At the base of Jinyong’s wall, Gao Changgong and a compact group of mounted guards reach the ditch after penetrating the siege lines. He lifts off a plain practical iron helmet, clearly revealing his face to suspicious defenders above. Once recognized, wall crossbowmen pivot their weapons outward toward distant Zhou troops, not toward him. No theatrical mask anywhere, no arrows striking bodies and no superhero charge.', '护城沟、辨认距离、弩手转向与头盔样式为视觉重构;五百骑入阵、城上不识、免胄示面后弩手救之有史载。'), sources: ['《北齐书·兰陵武王传》卷十一'] },
{ id: '04', title: '内外夹击·入阵曲后生', timePlace: '金墉城外战后', purpose: '交代解围结果与后世曲名的生成边界。', narration: '城上弩手与城外援军内外呼应,北周军退却。军士后来歌唱此战,形成兰陵王入阵曲。', overlay: { heading: '金墉解围', line: '这一战先成为军中歌声,后来才长成关于面具的传说。' }, people: [{ name: '高长恭', office: '北齐兰陵王', action: '与城上守军配合,打开围阵缺口' }, { name: '北齐军士', office: '援军与守军', action: '战后在城门外整队,以鼓拍记胜' }, { name: '北周军', office: '退军', action: '有序撤离远景,不画追杀' }], image: image('04-jinyong-relieved-soldiers-beat-rhythm', '金墉城门外北齐援军与守军会合,周军远撤;几名士卒敲小鼓、以盾柄击拍庆祝,高长恭戴普通头盔整军,无舞台面具。', 'After the siege breaks, Northern Qi relief troops link with Jinyong defenders outside the opened gate while Zhou formations withdraw across the winter plain in the far distance. A few soldiers beat small field drums and tap shield rims in a spontaneous marching rhythm, a restrained visual seed of the later “Entering the Battle” music. Gao Changgong has his plain helmet back on and reorganizes cavalry. No stage masks, choreographed dance or triumph over bodies.', '军士击拍、战后会师和周军撤退为视觉推演;金墉解围、齐人歌谣形成入阵曲之名有史载。'), sources: ['《北齐书·兰陵武王传》卷十一', '《周书·武帝纪上》卷五'] },
],
bgm: { catalogId: 'mingchangmian:mangshan_mianzhou#0', title: '邙山解围·免胄示面', version: '电影级配乐·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '第一幕以围城低音起,第二幕加入行军鼓,第三幕示面前骤停再由弩机节奏回应,第四幕把军中击拍化为入阵曲动机;删除歌词与品牌尾签。', reason: '项目曲目直接对应邙山免胄示面的正史节点,且可明确区分正史头盔与后世面具传说;仓库暂无成品音频。', cues: [cue('01','00:00','00:20','围城低音与冬风。'),cue('02','00:20','00:40','援军展开加入行军鼓。'),cue('03','00:40','01:01','示面前停顿,弩机声回应。'),cue('04','01:01','01:22','士卒击拍形成曲调动机。')] },
sources: [{ claim: '高长恭率五百骑入阵、城下免胄示面及金墉解围', citation: '《北齐书·兰陵武王传》卷十一', url: 'https://zh.wikisource.org/zh-hans/北齐书/卷011' }, { claim: '段韶等邙山援救与北齐军部署', citation: '《北齐书·段韶传》卷十六', url: 'https://zh.wikisource.org/zh-hans/北齐书/卷016' }, { claim: '北周围洛阳与退军', citation: '《周书·武帝纪上》卷五', url: 'https://zh.wikisource.org/zh-hans/周书/卷005' }],
blacklist: ['把“免胄”画成摘下面具;正史只说脱盔示面', '把五百骑画成单独歼灭全部周军,忽略段韶斛律光和城上弩手', '倒用唐代舞乐面具、戏曲靠旗或明清铠甲', '把兰陵王塑造成无甲无盔冲完整场战斗', '现代文字、水印、火器、魔法、尸堆与血腥'],
},
{
dir: '隋/隋灭陈之战(韩擒虎夜入建康)',
event: { canonicalName: '隋灭陈之战(韩擒虎夜入建康)', aliases: ['隋灭陈之战', '平陈之战', '韩擒虎入建康'], projectDynasty: '陈/隋', era: '隋文帝开皇八年至九年、陈后主祯明二年至三年', date: '公元588年冬至589年正月', place: '长江上游、广陵、采石、钟山与建康', summary: '隋文帝部署多路水陆军伐陈,杨素从上游牵制,贺若弼、韩擒虎分别从广陵和横江渡江;陈军在建康外围败溃,韩擒虎率军先入朱雀门,俘陈后主,南北重新统一。' },
narration: { language: 'zh-CN', estimatedSeconds: 87, fullText: '开皇八年冬,隋文帝杨坚下令大举伐陈。晋王杨广统筹诸军,杨素率水军从长江上游东下,牵制陈军江防;下游的贺若弼从广陵渡江,韩擒虎则由横江夜渡。隋军选择陈朝正月庆会、防务松弛之际行动,几路同时突破,而不是只靠一名将领潜入。贺若弼在钟山、白土冈一线击败陈军主力,韩擒虎攻取采石后直趋建康。陈后主仍在宫中,军报与命令被近臣阻隔,各部无法形成统一抵抗。韩擒虎所部先进入朱雀门,城中官军相继放下武器。陈后主与张丽华、孔贵人躲入一口枯井,隋军垂绳将他们拉出。这个细节见于正史,却不必拍成羞辱喜剧;它标志着一个朝廷已经失去最后的公开指挥空间。陈亡以后,隋军接收府库、图籍与州郡。自西晋末年长期分裂的南北,在589年重新统一。隋灭陈是多路战略、江防失效和陈朝政治失能共同造成的结果,不能缩写成“韩擒虎一夜一人灭国”。' },
scenes: [
{ id: '01', title: '八路伐陈·上下游并进', timePlace: '长江上游至淮南各军出发地', purpose: '建立隋军多路水陆协同。', narration: '杨广统筹伐陈,杨素上游水军东下,贺若弼、韩擒虎在下游等待渡江。', overlay: { heading: '一江多路', line: '建康面对的不是一处渡口,而是从上游到淮南同时收紧的战线。' }, people: [{ name: '杨广', office: '晋王、行军元帅', action: '统筹伐陈诸军' }, { name: '杨素', office: '行军元帅', action: '率上游水军顺江东下' }, { name: '贺若弼、韩擒虎', office: '行军总管', action: '分别在广陵、横江准备渡江' }], image: image('01-sui-armies-converge-on-yangtze', '冬季长江战略纵深画面,前景杨素木质水军顺峡江而下,远方平原两处隋军在不同渡口整备,杨广在简朴军府查看无字木牍。', 'A winter Yangtze campaign visual with natural depth: in the foreground Yang Su’s timber river fleet descends a broad upper-river reach; far across connected plains two separate Sui field armies prepare boats at different lower-river crossings. In a restrained open command pavilion Yang Guang, late 20s, reads blank wooden tallies with staff. Cinematic geography rather than a literal map, no floating labels or impossible simultaneous close-ups.', '上下游压缩同框、军府位置、船队与渡口数量为视觉化重构;杨广统军、杨素上游与贺若弼韩擒虎下游并进有史载。'), sources: ['《隋书·高祖纪下》卷二', '《隋书·韩擒虎贺若弼传》卷五十二'] },
{ id: '02', title: '正月渡江·江防失机', timePlace: '广陵、横江至采石', purpose: '表现隋军利用正月防务松弛渡江。', narration: '正月,贺若弼与韩擒虎分别渡江。陈军江防未能及时集中,采石很快失守。', overlay: { heading: '江面无人统合', line: '同一夜里两处渡江,陈军看到的是各自孤立的火点。' }, people: [{ name: '贺若弼', office: '吴州总管', action: '自广陵渡江' }, { name: '韩擒虎', office: '庐州总管', action: '自横江渡江并取采石' }, { name: '陈江防军', office: '沿江守军', action: '分散在各处,无法及时会合' }], image: image('02-new-year-night-crossings', '寒冷正月夜,两个相隔很远的江湾各有隋军浅底船静默渡江,韩擒虎在近景船头,远岸陈军烽火刚刚点起,不放烟花。', 'Cold first-month night on the Yangtze. In the near reach Han Qinhu, late 40s, broad and composed in dark Sui lamellar armor, crosses with infantry in low shallow boats; in a far separated river bend another line of boats implies Heruo Bi’s crossing. Chen beacons have only just ignited on the south bank. Moonlight, oars and breath vapor; no festival fireworks, giant navy, magic fog or troops walking on water.', '两处渡江同框、舟具、烽火反应与人物外貌为视觉重构;贺若弼广陵渡江、韩擒虎横江渡江取采石有史载。'), sources: ['《隋书·韩擒虎贺若弼传》卷五十二', '《资治通鉴》卷一百七十七'] },
{ id: '03', title: '钟山决战·朱雀门开', timePlace: '建康钟山、白土冈与朱雀门', purpose: '用外围败局解释韩擒虎为何能先入城。', narration: '贺若弼在建康外围击溃陈军,韩擒虎直抵朱雀门。守军放下武器,隋军入城。', overlay: { heading: '朱雀门入', line: '外线主力一散,首都城门就只剩下选择何时打开。' }, people: [{ name: '贺若弼', office: '隋军主将', action: '在钟山白土冈一线击败陈军' }, { name: '韩擒虎', office: '隋军主将', action: '率军直趋朱雀门并先入建康' }, { name: '任蛮奴等', office: '陈军守将', action: '见大势已去,率部放下武器' }], image: image('03-zhuque-gate-opens', '清晨建康朱雀门,陈军守卒把长矛与弓整齐放在门侧,厚木门缓缓开启,韩擒虎率少量先头步骑有序进入,远处城外烟尘暗示外围战败。', 'Dawn at Jiankang’s restrained Zhuque Gate. Chen defenders stack spears and bows beside the gatehouse as the heavy timber doors open; Han Qinhu leads a limited Sui vanguard through in orderly ranks rather than storming. Beyond the wall distant dust and retreating formations imply Heruo Bi’s victory outside. Civilian streets remain closed and quiet. No triumphant slaughter, palace-scale gate or readable signboard.', '受降动作、先头军规模、门楼形制和外围烟尘为视觉推演;贺若弼破陈军、韩擒虎先入朱雀门有史载。'), sources: ['《隋书·韩擒虎贺若弼传》卷五十二', '《陈书·后主纪》卷六'] },
{ id: '04', title: '井中出降·府库接收', timePlace: '建康宫城与官署', purpose: '以克制方式交代陈后主被俘与统一后的接管。', narration: '陈后主藏入枯井,隋军垂绳将他拉出。随后府库图籍被封存接收,陈朝灭亡。', overlay: { heading: '井底无朝廷', line: '当皇帝躲进井里,地面上的府库、城门与军队已经换了主人。' }, people: [{ name: '陈后主陈叔宝', office: '南陈皇帝', action: '藏井后被发现并拉出;不作喜剧羞辱' }, { name: '张丽华、孔贵人', office: '陈后主妃嫔', action: '同在井中;人物保持完整衣着与尊严' }, { name: '韩擒虎', office: '隋军主将', action: '控制宫城、接收降人和府库' }], image: image('04-well-surrender-and-archives-sealed', '建康宫院,一口干井旁隋军垂下粗绳,陈后主在井口下方被温和拉起,只露上身且衣冠完整;画面另一侧官吏封存竹简箱与印匣。', 'In a modest Jiankang palace courtyard, Sui soldiers lower a thick rope into a dry masonry well. Chen Shubao is visible just below the rim being helped upward, fully clothed in dark court robes and treated as a captive ruler, while the two women remain only implied deeper in shadow to avoid spectacle. On the other side officials seal bamboo-document chests and tally cases. No mockery, overcrowding, exposed bodies or execution.', '井深、救出姿态、人物可见程度与图籍接收并置为视觉重构;陈后主与二妃藏井被引出、隋接收建康有史载。'), sources: ['《隋书·韩擒虎传》卷五十二', '《陈书·后主纪》卷六', '《资治通鉴》卷一百七十七'] },
],
bgm: { catalogId: 'zhanzhen:pingchen#1', title: '隋灭陈·平陈之战', version: '韩擒虎版·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '第一幕以江面多路动机铺开,夜渡时压低打击,朱雀门开启形成短峰,井中出降立即削至低弦与封箱声;删除歌词与品牌尾签。', reason: '项目有平陈专属韩擒虎版,适合本包下游突破与先入建康的视角;仓库暂无成品音频。', cues: [cue('01','00:00','00:22','上下游主题分层出现。'),cue('02','00:22','00:43','夜渡以桨声和低弦推进。'),cue('03','00:43','01:05','城门开启形成短峰。'),cue('04','01:05','01:27','井口与封存图籍冷收。')] },
sources: [{ claim: '隋文帝伐陈部署与平陈结果', citation: '《隋书·高祖纪下》卷二', url: 'https://zh.wikisource.org/zh-hans/隋书/卷002' }, { claim: '贺若弼韩擒虎渡江、入建康及俘陈后主', citation: '《隋书·韩擒虎贺若弼传》卷五十二', url: 'https://zh.wikisource.org/zh-hans/隋书/卷052' }, { claim: '陈方守备、后主出降与亡国', citation: '《陈书·后主纪》卷六', url: 'https://zh.wikisource.org/zh-hans/陈书/卷006' }, { claim: '多路进军的日序与建康终局', citation: '《资治通鉴》卷一百七十七', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷177' }],
blacklist: ['把隋灭陈写成韩擒虎单人潜入、一夜独力灭国', '把贺若弼与韩擒虎的渡江地点和战功合并成同一路', '把后主藏井拍成裸身、三人拥挤或滑稽羞辱', '使用烟花、火炮或后世巨型战船', '现代文字、水印、明清宫城服甲、尸堆与血腥'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((person) => `${person.name}(${person.office};${person.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((item) => `| ${item.from}–${item.to} | ${item.scene} | ${item.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Eastern Jin and Northern-Southern core battle packages`)
@@ -0,0 +1,96 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed eleventh-to-thirteenth-century East Asia, cinematic 16:9 landscape, subtle film grain, physically plausible troops, cities, rivers, ships and logistics, packed earth, aged timber, hemp, low-sheen silk, leather, iron lamellar armor, swords, bows, crossbows, spears, cavalry tack and wooden carts. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, modern firearms, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Southern Song, Western Xia and Jin story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const person = (name, office, action) => ({ name, office, action })
const cue = (scene, from, to, direction) => ({ scene, from, to, direction })
const packs = [
{
dir: '南宋/襄阳之战(樊城陷落至吕文焕出降)',
event: { canonicalName: '襄阳之战(樊城陷落至吕文焕出降)', aliases: ['襄阳之战', '襄樊之战', '襄樊陷落'], projectDynasty: '南宋', era: '宋度宗咸淳三年至九年、元世祖至元四年至十年', date: '公元1267年至1273年', place: '汉水襄阳、樊城及外围堡寨', summary: '元军长期围困襄阳、樊城,以沿江堡寨、水军和回回炮逐步切断宋军援路;樊城失守后,襄阳守将吕文焕出降,汉水通道由此向元军打开,南宋长江上游防线失去核心。' },
narration: { language: 'zh-CN', estimatedSeconds: 87, fullText: '咸淳三年起,元军把襄阳、樊城作为突破南宋的关键。两城隔汉水相望,既是荆襄军镇,也是四川与长江中下游之间的枢纽。元军没有只靠一次强攻,而是在多年间修筑堡寨、控制汉水、拦截宋军援船,让城内补给越来越困难。宋军多次沿江救援,都没能稳定打通通道。围城后期,元军调来威力更大的回回炮,集中攻击樊城防御。咸淳九年正月,樊城被攻破,守将牛富、范天顺战死。隔江的襄阳失去最重要的屏障,吕文焕仍然坚持了一段时间,却已看不到可靠援军。二月,吕文焕开城出降。长达数年的襄樊之战到此结束。它不是一台投石机突然砸开南宋,而是围城、造船、筑垒、断援和指挥失误长期叠加的结果。襄阳一失,元军获得熟悉长江水战的军队和顺流东下的通道,南宋从局部守城转入无法逆转的全面退却。' },
scenes: [
{ id: '01', title: '环城筑垒·汉水断援', timePlace: '襄阳、樊城与汉水两岸', purpose: '建立多年封锁的整体结构,而非一场短促攻城。', narration: '元军在两岸建立堡寨和水军据点,逐步截断宋军沿汉水运粮、增援的通道。', overlay: { heading: '围城不是围墙', line: '真正被锁住的是渡口、粮船和每一条能把襄阳接回南宋的道路。' }, people: [person('阿术', '元军主将', '主持襄樊方向长期围困'), person('刘整', '降元水军将领', '建议造船练水军并封锁汉水'), person('吕文焕', '南宋襄阳守将', '组织两城防御与求援')], image: image('01-yuan-forts-and-fleet-isolate-xiangyang', '宽阔汉水两岸,襄阳和樊城两座宋代城池隔水相望,元军在外围分散修筑小型木土堡寨,水中巡船检查运粮船;城内烽火回应但无大规模攻城。', 'Across the broad Han River, period-appropriate Xiangyang and Fancheng face one another while Yuan forces build multiple small earth-and-timber forts outside and patrol boats inspect the channel. Signal fires answer from the cities, but no grand assault is underway. Show a years-long blockade through layered logistics and geography, no giant continuous wall, modern harbor, gunpowder cannon or fantasy citadel.', '两城相对位置、堡寨疏密与巡船检查为视觉重构;元军长期筑垒、造船并封锁汉水有史载。'), sources: ['《宋史·度宗本纪》卷四十六', '《元史·阿术传》卷一百二十八'] },
{ id: '02', title: '回回炮至·樊城先陷', timePlace: '樊城城外与汉水北岸', purpose: '呈现攻城技术加强和樊城失守,但不把器械神化。', narration: '围城后期,元军以回回炮轰击樊城。城墙受损后元军攻入,牛富、范天顺战死。', overlay: { heading: '先失樊城', line: '新式投石机扩大了缺口,真正让守城失败的仍是多年断援后的孤立。' }, people: [person('亦思马因、阿老瓦丁', '元军回回炮匠', '参与制造和使用大型配重投石机'), person('牛富', '南宋樊城守将', '率军抵抗并战死;不展示遗体'), person('范天顺', '南宋将领', '守樊城战死;不展示死亡')], image: image('02-counterweight-trebuchets-breach-fancheng', '冬日樊城外,两座结构真实的木质配重投石机在安全距离投射石弹,一段城墙已出现坍塌缺口;守军在内侧用木栅封堵,汉水对岸襄阳可见,不画火球爆炸。', 'Winter outside Fancheng. Two historically plausible counterweight trebuchets launch plain stone projectiles from a safe distance; one section of the city wall has a damaged breach while Song defenders construct an inner timber barricade. Xiangyang remains visible across the Han River. Show engineering within a long siege, not flaming explosive boulders, modern cannon, a single magical shot, gore or collapsing whole city.', '投石机具体结构、数量与缺口形态为视觉推演;回回炮投入、樊城被破及牛富等战死有史载。'), sources: ['《元史·亦思马因传》卷二百三', '《宋史·度宗本纪》卷四十六'] },
{ id: '03', title: '隔江无援·襄阳出降', timePlace: '襄阳城门与汉水码头', purpose: '交代吕文焕出降和汉水通道打开的结果。', narration: '樊城陷落后,襄阳独守无援。吕文焕最终出降,元军接管城门、船坞与军籍。', overlay: { heading: '六年围城结束', line: '城门打开的同时,通往长江中下游的水路也向元军打开。' }, people: [person('吕文焕', '南宋京西安抚副使、襄阳守将', '在孤城无援后出降'), person('阿术', '元军主将', '接受投降并接收城防'), person('襄阳军民', '守军与居民', '交出军械、维护城内秩序')], image: image('03-lu-wenhuan-surrenders-xiangyang', '阴天襄阳城门缓慢开启,吕文焕在无字军籍木案旁向阿术交出城门钥匙,宋军成排放置弩机和长枪;汉水码头的元军船只开始有序进驻,不画跪地羞辱。', 'Under an overcast sky, Xiangyang’s gate opens slowly. Lü Wenhuan stands upright beside blank military rolls and presents the gate key to Aju; Song soldiers stack crossbows and spears in orderly rows. At the Han River wharf, Yuan vessels begin a controlled entry. Convey the opening of a strategic corridor without kneeling humiliation, triumphal looting, readable surrender text or anachronistic banners.', '交钥匙、武器堆放与船只进驻为视觉转译;樊城陷后吕文焕出降、元军接管襄阳有史载。'), sources: ['《宋史·度宗本纪》卷四十六', '《元史·阿术传》卷一百二十八'] },
],
bgm: { catalogId: 'zhanzhen:xiangyang#0', title: '襄阳之战', version: '电影级配乐·专属器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '多年封锁以低频脉冲表现,回回炮只用木索和石击声,樊城缺口形成一次峰值,襄阳出降立即转冷;删除人声、歌词和品牌尾签。', reason: '项目已有襄阳之战专属电影级配乐,围困—破城—出降结构与本包对应;仓库暂无成品音频。', cues: [cue('01','00:00','00:29','水路封锁以低频脉冲推进。'), cue('02','00:29','00:58','木索绷紧与石击形成唯一峰值。'), cue('03','00:58','01:27','城门开启后转为冷弦收尾。')] },
sources: [{ claim: '樊城陷落、吕文焕出降与襄阳失守', citation: '《宋史·度宗本纪》卷四十六', url: 'https://zh.wikisource.org/zh-hans/宋史/卷046' }, { claim: '阿术围襄阳和接受出降', citation: '《元史·阿术传》卷一百二十八', url: 'https://zh.wikisource.org/zh-hans/元史/卷128' }, { claim: '回回炮匠与攻樊城', citation: '《元史·亦思马因传》卷二百三', url: 'https://zh.wikisource.org/zh-hans/元史/卷203' }],
blacklist: ['把六年围困压成一台投石机一击破城', '把回回炮画成火炮、火箭或爆炸石弹', '把襄阳和樊城画在同一岸或混成一座城', '只怪吕文焕个人投降,忽略樊城陷落与长期断援', '现代文字、水印、明清城墙服甲、现代火器、尸堆与血腥'],
},
{
dir: '南宋/崖山海战(宋亡)',
event: { canonicalName: '崖山海战(宋亡)', aliases: ['崖山海战', '崖门之战', '崖山之亡'], projectDynasty: '南宋', era: '宋少帝祥兴二年、元世祖至元十六年', date: '公元1279年二月', place: '广东崖山、崖门海域', summary: '元将张弘范率水军封锁崖山,南宋流亡朝廷与军民聚于联结船阵;决战中宋军船阵崩溃,陆秀夫负少帝赵昺投海,南宋灭亡。' },
narration: { language: 'zh-CN', estimatedSeconds: 83, fullText: '临安投降后,南宋残余力量拥立幼主一路南撤,最终聚集在广东崖山。这里不只有军队,还有流亡朝廷、官员家属、工匠和百姓。张世杰把大量船只联结成水上营垒,希望让各船彼此支援;元将张弘范则封锁出海口,切断淡水和补给。祥兴二年二月,元军从多面进攻。船阵相连可以稳住防线,也让局部失火、破口和恐慌更难隔离。战斗持续到傍晚,宋军阵线瓦解,船只无法有序突围。丞相陆秀夫背起七岁的少帝赵昺投海,许多军民随后死于海中。张世杰带少量船只突围,后来在风浪中覆舟。崖山海战结束了南宋朝廷,也使元朝完成对全国主要地区的统一。它不应只被缩成一句“十万人跳海”的悲壮传说;真正的崖山是一座携带朝廷和普通家庭的漂浮城市,在封锁、缺水与决战中一起失去了退路。' },
scenes: [
{ id: '01', title: '流亡至海·联舟成营', timePlace: '崖山内港与崖门水道', purpose: '表现军民混居的流亡朝廷和联结船阵。', narration: '南宋流亡朝廷抵达崖山,张世杰把军船、民船联结成营,官民都生活在狭窄船阵里。', overlay: { heading: '最后一座都城在海上', line: '船里装的不只是士兵,还有一整个朝廷和无处再退的家庭。' }, people: [person('张世杰', '南宋枢密副使、主帅', '组织联结船阵守崖门'), person('赵昺', '南宋少帝', '随流亡朝廷居于船中'), person('陆秀夫', '左丞相', '维持朝仪并照料少帝'), person('流亡军民', '官属、工匠与百姓', '在船阵中取水、做饭和修船')], image: image('01-floating-song-court-at-yashan', '潮湿晨雾中的崖山内港,大量宋代海船和民船以粗缆相连,船上同时有甲士、修船工匠、炊烟和家庭;陆秀夫陪少帝站在克制主船廊下,张世杰检查缆索。', 'In humid morning mist inside Yashan harbor, Song sea-going warships and civilian craft are connected by heavy ropes. Soldiers, repair workers, cooking fires and displaced families share cramped decks. Lu Xiufu stands with the child emperor beneath a restrained command-vessel awning while Zhang Shijie inspects moorings. Show a floating refugee court, no luxury palace ship, giant chain, modern junk design or celebratory flags.', '具体联船方式、生活细节与人物同船为视觉重构;宋廷军民聚于崖山、张世杰联舟为阵有史载。'), sources: ['《宋史·瀛国公本纪》卷四十七', '《宋史·张世杰传》卷四百五十一'] },
{ id: '02', title: '封锁决战·船阵崩溃', timePlace: '崖门水道与宋军船阵', purpose: '呈现元军封锁和多面进攻下的失序,不依赖奇观爆炸。', narration: '张弘范围住崖门并发动决战。相连船阵出现破口,烟、潮流与拥挤使宋军无法有序突围。', overlay: { heading: '相连也意味着无处避开', line: '联舟让防线更稳,也让一个破口沿着缆索传遍整座海上营垒。' }, people: [person('张弘范', '元军水军统帅', '封锁出海口并组织多面进攻'), person('张世杰', '南宋水军主帅', '指挥联舟阵抵抗并寻找突围口'), person('宋元水军', '桨手、弩手与接舷甲士', '在潮流中争夺船阵边缘')], image: image('02-yuan-attack-breaks-linked-song-fleet', '傍晚崖门潮水中,元军船队从两侧逼近,宋军联结船阵的一段缆索断裂、数艘船挤压转向,甲板有烟但没有爆炸大火;张世杰在高处传令。', 'At dusk in the tidal Yamen channel, Yuan vessels press from two sides while one section of the linked Song formation breaks a mooring rope and several ships crowd during a turn. Smoke hangs over decks without a spectacular inferno; Zhang Shijie signals from a raised platform. Show naval pressure, tide and congestion, no modern cannon broadside, exploding ships, drowned bodies or impossible armada.', '缆索断裂、船位挤压与进攻方向为视觉推演;元军封锁并多面进攻、宋军船阵溃败有史载。'), sources: ['《元史·世祖本纪》卷十', '《宋史·瀛国公本纪》卷四十七'] },
{ id: '03', title: '少帝入海·南宋终局', timePlace: '崖山主船与外海方向', purpose: '克制呈现陆秀夫负帝投海及政权终结。', narration: '船阵崩溃后,陆秀夫负少帝赵昺投海。张世杰率少量船只突围,南宋朝廷至此终结。', overlay: { heading: '海上无路', line: '国玺、仪仗和最后一位皇帝都消失在潮水里,留下的不是神话而是亡国。' }, people: [person('陆秀夫', '南宋左丞相', '背负少帝投海;不展示入水瞬间'), person('赵昺', '南宋少帝', '随陆秀夫殉国'), person('张世杰', '南宋主帅', '率少量船只突围,后遇风覆舟')], image: image('03-lu-xiufu-carries-child-emperor-to-sea', '暮色宋军主船尾部,陆秀夫背着穿朴素礼服的少帝面向空旷海面,近景案上留下无字木牍和收起的印匣;远处少量残船驶向外海,不画跳海、溺亡或尸体。', 'At twilight on the stern of the failing Song command ship, Lu Xiufu carries the child emperor on his back and faces the open sea. A closed seal box and blank court tallies remain on a low table; a few surviving vessels sail toward the outer water in the distance. Frame the last decision before entering the sea, never the jump itself, drowning, bodies, halo, triumph or romanticized suicide spectacle.', '人物姿态、印匣与残船外逃为克制视觉转译;陆秀夫负帝投海、张世杰突围及宋亡有史载。'), sources: ['《宋史·瀛国公本纪》卷四十七', '《宋史·陆秀夫传》卷四百五十一'] },
],
bgm: { catalogId: 'zhanzhen:yashanhaizhan#0', title: '崖山海战', version: '电影级配乐·专属器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '联舟营以低频潮声和绳索摩擦开场,决战只形成一次短促峰值,终局抽至海风和单弦;删除人声、歌词和品牌尾签。', reason: '项目已有崖山海战专属电影级配乐,流亡、海战与亡国三段完全匹配;仓库暂无成品音频。', cues: [cue('01','00:00','00:28','潮声、木船与生活噪声建立漂浮朝廷。'), cue('02','00:28','00:55','缆索断裂和鼓点形成唯一峰值。'), cue('03','00:55','01:23','抽空至海风与单弦,不煽动殉亡。')] },
sources: [{ claim: '崖山战败、陆秀夫负帝入海与宋亡', citation: '《宋史·瀛国公本纪》卷四十七', url: 'https://zh.wikisource.org/zh-hans/宋史/卷047' }, { claim: '张世杰联舟、突围与后续覆舟', citation: '《宋史·张世杰传》卷四百五十一', url: 'https://zh.wikisource.org/zh-hans/宋史/卷451' }, { claim: '元军攻崖山的元方纪年', citation: '《元史·世祖本纪》卷十', url: 'https://zh.wikisource.org/zh-hans/元史/卷010' }],
blacklist: ['把崖山只写成“十万人同时跳海”的整齐壮烈场面', '把联舟画成铁链锁船或现代军舰编队', '直接展示儿童投海、溺亡、漂尸或自杀美化', '把张弘范误写为蒙古人或忽略其汉人降将身份', '现代文字、水印、明清船型服甲、现代火炮、尸堆与血腥'],
},
{
dir: '西夏/好水川之战(元昊设伏破任福)',
event: { canonicalName: '好水川之战(元昊设伏破任福)', aliases: ['好水川之战', '好水川之败', '元昊破任福'], projectDynasty: '西夏', era: '宋仁宗庆历元年、西夏天授礼法延祚四年', date: '公元1041年二月', place: '渭州笼竿城、六盘山好水川一带', summary: '西夏元昊诱使宋将任福率军深入好水川,在山地预设伏兵并切断宋军退路;宋军疲惫缺粮后遭夹击,任福等战死,西北战局转入更依赖堡寨与长期防御的阶段。' },
narration: { language: 'zh-CN', estimatedSeconds: 82, fullText: '庆历元年,西夏元昊再次进攻宋朝西北。陕西经略安抚副使韩琦希望主动截击,命任福率军从怀远城方向出发,要求在指定地区会师,不要轻易越过既定路线。西夏军先以小股部队接触后退,任福追击,逐渐进入六盘山附近的好水川。宋军连续行军,粮食和队形都被拉长,却没有及时发现元昊主力已在山谷两侧等待。二月十四日,西夏伏兵从高地和谷口同时出现,宋军前后受压。任福中箭受伤,仍率部作战,最终战死;桑怿、刘肃等将领也未能突围。宋军损失惨重。传说中的木盒、鸽子可以作为史料争议中的细节,不应取代更可靠的战场逻辑:诱敌、深入、疲惫和山地合围。好水川之败使宋廷震动,也暴露了前线将帅之间命令不一、急于求战和边地情报不足的问题。此后宋夏继续交战,最终通过庆历和议暂时稳定边境。' },
scenes: [
{ id: '01', title: '奉令出塞·追兵渐深', timePlace: '怀远城至六盘山道路', purpose: '建立主动截击和追逐拉长补给的过程。', narration: '任福奉韩琦命出兵。西夏小部接战即退,宋军追入山地,队伍与粮道逐渐拉长。', overlay: { heading: '敌军一直在退', line: '每一次看似可以追上的后撤,都让宋军离会师地点和补给更远。' }, people: [person('韩琦', '陕西经略安抚副使', '部署诸军截击并规定会师方向'), person('任福', '环庆路副都部署', '率宋军追击西夏小部'), person('西夏诱敌部队', '轻骑与步骑混合前锋', '保持接触后向好水川方向撤退')], image: image('01-ren-fu-pursues-into-liupan-mountains', '早春六盘山道路,任福率宋军沿狭长谷地追击远处有序后撤的西夏轻骑,后方驮队与步兵已经拉开很长距离;韩琦只以营中无字军令木牍在远景提示,不作分身。', 'Early spring in the Liupan Mountains. Ren Fu leads Song troops along a narrowing valley after a small Western Xia mounted screen withdrawing in good order. The Song pack train and infantry are visibly stretched far behind. A distant courier carrying blank orders hints at Han Qi’s plan without placing him magically on the field. Show temptation and extension, no frantic rout, literal trap arrows or desert stereotype.', '追逐距离、队伍拉长与传令为视觉重构;任福奉命出师、追逐夏军深入好水川有史载。'), sources: ['《宋史·任福传》卷三百二十五', '《宋史·夏国传》卷四百八十五'] },
{ id: '02', title: '山谷伏发·前后受敌', timePlace: '好水川谷地', purpose: '呈现伏兵利用高地与谷口夹击,不神化机关。', narration: '宋军疲惫进入好水川,西夏主力从山脊和谷口现身,任福部来不及展开完整阵线。', overlay: { heading: '退路先被看见', line: '伏兵不是从地下冒出,而是在宋军进入谷底前就占住了能够俯瞰道路的位置。' }, people: [person('元昊', '西夏皇帝', '部署诱敌和山谷伏击'), person('任福', '宋军主将', '组织受压部队结阵'), person('西夏伏兵', '山地步骑', '从两侧高地和谷口压迫宋军')], image: image('02-western-xia-ambush-closes-haoshuichuan', '好水川狭谷中,疲惫宋军刚把盾弩阵转向两侧,西夏步骑已出现在左右山脊和后方谷口;元昊在远处高地观察,地面没有木盒机关或鸽群。', 'In the narrow Haoshuichuan valley, exhausted Song troops turn shields and crossbows toward both slopes while Western Xia infantry and cavalry appear on left and right ridges and at the rear mouth of the pass. Yuanhao observes from distant high ground. Make the ambush understandable through terrain and timing, with no magical emergence, pigeon-box gimmick, collapsing cliffs, cloned armies or bodies.', '伏兵位置、宋军转阵与元昊观察点为视觉推演;诱敌深入、伏兵夹击和宋军仓促应战有史载。'), sources: ['《宋史·任福传》卷三百二十五', '《续资治通鉴长编》庆历元年条'] },
{ id: '03', title: '任福战死·边策转守', timePlace: '好水川战后与宋西北堡寨', purpose: '交代主将战死和宋军战略转向,避免阵亡奇观。', narration: '任福等将领战死,残部退出山谷。宋廷随后加强堡寨和守备,宋夏战争继续到庆历和议。', overlay: { heading: '山谷之外还要守下去', line: '战败留下的不只是名单,还有此后每一座边寨更谨慎的门和粮仓。' }, people: [person('任福', '宋军主将', '负伤战死;以遗留兵符暗示'), person('宋军残部', '败退将卒', '收拢伤员并返回边寨'), person('宋西北守军', '堡寨军民', '补充粮仓、加固木栅')], image: image('03-song-survivors-return-to-frontier-forts', '暮色山口,宋军残部护送伤员和一面收起的无字将旗返回小型边寨,近景担架上只盖披风不见伤口;寨内军民正补粮、修木栅,案上留下任福无字兵符。', 'At dusk, Song survivors escort wounded comrades and a furled blank command standard back toward a modest frontier fort. Stretchers are covered, with no visible wounds. Inside the gate, soldiers and families restock grain and repair timber defenses; Ren Fu’s blank command tally remains on a table. Shift from battlefield loss to defensive policy, no corpse display, execution, triumph or instant peace ceremony.', '兵符、残部返寨与补粮修栅为结果性视觉转译;任福等战死、宋军大败及后续加强边防有史载。'), sources: ['《宋史·任福传》卷三百二十五', '《宋史·夏国传》卷四百八十五'] },
],
bgm: { catalogId: 'zhanzhen:yinshan#0', title: '唐灭东突厥·阴山之战', version: '电影级配乐·北地诱敌结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删除唐突厥人物动机和人声,只取北地风声、稀疏马蹄与山谷合围结构;战败后迅速降为边寨木工声。', reason: '项目暂无好水川专曲,阴山曲可提供北地骑战空间,但需避免把党项军画听成突厥或蒙古。仓库暂无成品音频。', cues: [cue('01','00:00','00:27','稀疏马蹄拉长追击距离。'), cue('02','00:27','00:55','两侧节拍合拢形成短峰。'), cue('03','00:55','01:22','风声退去,边寨木工声收尾。')] },
sources: [{ claim: '任福出师、深入好水川与战死', citation: '《宋史·任福传》卷三百二十五', url: 'https://zh.wikisource.org/zh-hans/宋史/卷325' }, { claim: '元昊与宋夏战事的西夏传记载', citation: '《宋史·夏国传》卷四百八十五', url: 'https://zh.wikisource.org/zh-hans/宋史/卷485' }, { claim: '庆历元年好水川战事编年', citation: '《续资治通鉴长编》', url: 'https://zh.wikisource.org/zh-hans/续资治通鉴长编' }],
blacklist: ['把木盒放鸽的争议细节画成决定胜负的神奇机关', '把党项西夏军画成蒙古帝国或清代骑兵', '把任福追击写成没有上级部署和会师约束的个人冲动', '直接展示任福中箭死亡或满谷尸体', '现代文字、水印、明清服甲、现代火器与血腥'],
},
{
dir: '西夏/永乐城之战(夏军围城破宋)',
event: { canonicalName: '永乐城之战(夏军围城破宋)', aliases: ['永乐城之战', '永乐城之败', '夏破徐禧军'], projectDynasty: '西夏', era: '宋神宗元丰五年、西夏大安八年', date: '公元1082年九月', place: '银州故地永乐城、无定河一带', summary: '宋将徐禧在西夏边地仓促修筑永乐城,城防与水源尚未完备即遭西夏大军包围;夏军控制水源并发动攻城,永乐城陷落,徐禧等战死,宋朝此前五路伐夏所得优势受到重挫。' },
narration: { language: 'zh-CN', estimatedSeconds: 84, fullText: '元丰五年,宋朝在前一年五路伐夏之后,试图把新取得的横山地区变成永久防线。给事中徐禧选择在银州故地附近修筑永乐城,希望从这里控制通往西夏腹地的道路。城池修得很快,却离可靠水源较远,外围堡寨和补给体系也没有完全建立。西夏方面很快集结军队反攻。夏军包围永乐,控制无定河方向的取水通道,并击败前来增援的宋军。城内人口和军马在缺水中迅速失去战斗力。徐禧仍试图守城,但城墙在连续攻击下被突破。永乐城陷落,徐禧、李舜举等官员将领战死,大量军民未能撤出。宋神宗收到败报后震动,朝廷对西北扩张转为谨慎。永乐城之败不是一座城“选址不好”这么简单:它暴露了快速筑城、深入占领与后方运输之间的矛盾。宋军可以在地图上向前推进一座城,却未必能在沙碛、河谷和敌军反攻中持续供水、供粮并保住它。' },
scenes: [
{ id: '01', title: '急筑永乐·水源在外', timePlace: '银州故地与无定河边', purpose: '建立城池仓促修筑和水源距离这一结构问题。', narration: '徐禧督建永乐城,土墙和仓库迅速成形,取水路线却延伸到城外河谷。', overlay: { heading: '城先建好,水还没接上', line: '边墙能在数周内合拢,日复一日的粮与水却必须经过敌境。' }, people: [person('徐禧', '宋给事中、永乐城主持者', '选址并督促快速筑城'), person('宋军役夫', '军士、民夫与工匠', '夯土筑墙、修仓与运水'), person('西夏斥候', '侦察骑兵', '从远处观察取水和运输路线')], image: image('01-song-builds-yongle-fort-away-from-water', '干燥横山边地,宋军役夫正在夯筑规模克制的永乐城土墙,城内仓库尚未完工;一列驮水骡车沿长坡通向远处无定河,山脊上可见少量西夏斥候。', 'In the dry Hengshan frontier, Song laborers rapidly ram earth for the modest Yongle fort while storehouses remain unfinished. A long line of water pack animals descends toward the distant Wuding River; a few Western Xia scouts observe from a ridge. Make the exposed water route visually central. No mature giant city, modern aqueduct, desert fantasy, construction machinery or combat yet.', '城墙完成度、仓库状态、河距与斥候位置为视觉重构;徐禧筑永乐城、取水不便与夏军侦察有史载。'), sources: ['《宋史·神宗本纪》卷十六', '《宋史·徐禧传》卷三百三十四'] },
{ id: '02', title: '夏军合围·断绝取水', timePlace: '永乐城外与无定河通道', purpose: '呈现围城通过控制水路削弱守军,而非神秘诅咒。', narration: '西夏军占据城外高地和取水道路,击退宋军援兵。城中缺水,军马和守军迅速疲惫。', overlay: { heading: '最先失守的是水路', line: '城墙还没有破,水桶已经回不来了。' }, people: [person('西夏统军', '梁氏政权前线将领', '组织围城并控制无定河通道'), person('徐禧', '永乐城守方主事', '调兵守城并等待援军'), person('宋军运水队', '役夫与护送军士', '在敌军控制道路后被迫退回')], image: image('02-western-xia-cuts-water-to-yongle', '烈日下的永乐城外,西夏骑兵占据通往河谷的坡道,宋军运水队推着空桶和驮架退回城门;城头守军把最后几桶水分给军马,远处援军被阻在尘线之外。', 'Under a harsh sun outside Yongle, Western Xia cavalry controls the slope toward the river while a Song water detail retreats with empty barrels and pack frames. At the gate, defenders ration the last visible buckets between soldiers and horses; a relief column remains blocked in distant dust. Show siege logistics and thirst without starving caricature, miraculous drought, giant army wall or violence.', '道路控制、空桶、分水与援军距离为视觉转译;夏军围城、断取水并败宋援军有史载。'), sources: ['《宋史·徐禧传》卷三百三十四', '《宋史·夏国传》卷四百八十六'] },
{ id: '03', title: '城陷人散·西北转守', timePlace: '永乐城破口与宋军后方堡寨', purpose: '交代城破、将领战死和宋廷战略受挫。', narration: '永乐城在缺水和猛攻中陷落,徐禧等战死。败报传回后,宋廷重新评估深入西夏的筑城战略。', overlay: { heading: '一座新城没有撑过秋天', line: '地图上向前的一格,最后变成了后方每座堡寨都要重新计算的距离。' }, people: [person('徐禧', '宋给事中', '城陷时战死;不展示遗体'), person('李舜举', '宋内侍、监军', '与守军共同战死;不展示死亡'), person('宋军残部与后方官吏', '撤退者和边防经手者', '携败报返回并复核粮水路线')], image: image('03-yongle-falls-and-frontier-reassesses', '黄昏永乐城一段土墙坍塌,西夏军从缺口有序进入,宋军残部从另一侧护送伤员撤离;前景驿骑携无字败报奔向远处后方堡寨,堡寨官吏查看水桶和粮袋。', 'At dusk, one section of Yongle’s earthen wall has collapsed and Western Xia troops enter in controlled formation while Song survivors escort covered wounded people out along another route. In the foreground a courier with a blank defeat report rides toward a distant rear fort where officials inspect water barrels and grain. Use depth to connect defeat and policy reassessment; no citywide blaze, bodies, execution or triumphant slaughter.', '破口、双向队伍与后方复核为跨时段视觉压缩;永乐城陷、徐禧等死与宋廷受挫有史载。'), sources: ['《宋史·神宗本纪》卷十六', '《宋史·徐禧传》卷三百三十四'] },
],
bgm: { catalogId: 'zhanzhen:anshicheng#0', title: '唐太宗征高句丽·安市城之战', version: '电影级配乐·筑城与补给失败结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删除辽东、唐代人物动机与全部人声,只取夯土劳动、补给收紧和撤退弧线;城陷处不做英雄化爆燃。', reason: '项目暂无永乐专曲,安市城曲能表现工程、补给和攻守受挫;需明确两战时代与地点完全不同。仓库暂无成品音频。', cues: [cue('01','00:00','00:28','夯土节奏与驮水铃声。'), cue('02','00:28','00:56','节拍收窄表现断水。'), cue('03','00:56','01:24','城破短峰后退为驿骑与风声。')] },
sources: [{ claim: '永乐城陷与徐禧等战死', citation: '《宋史·神宗本纪》卷十六', url: 'https://zh.wikisource.org/zh-hans/宋史/卷016' }, { claim: '徐禧筑城、围城与失败经过', citation: '《宋史·徐禧传》卷三百三十四', url: 'https://zh.wikisource.org/zh-hans/宋史/卷334' }, { claim: '西夏方面相关战事', citation: '《宋史·夏国传》卷四百八十六', url: 'https://zh.wikisource.org/zh-hans/宋史/卷486' }],
blacklist: ['把永乐城画成经营多年的巨大成熟城市', '把断水画成超自然旱灾或河流突然消失', '把西夏军画成蒙古帝国军队', '把败因只归于选址,忽略快速扩张、援军和运输体系', '现代文字、水印、明清服甲、现代火器、尸堆与血腥'],
},
{
dir: '金/采石之战(虞允文拒金)',
event: { canonicalName: '采石之战(虞允文拒金)', aliases: ['采石之战', '采石矶之战', '海陵王南伐'], projectDynasty: '金', era: '宋高宗绍兴三十一年、金海陵王正隆六年', date: '公元1161年十一月', place: '长江采石矶、和州至瓜洲一线', summary: '金海陵王完颜亮大举南侵并在采石准备渡江,宋中书舍人虞允文临时整合江防水军,以战船和弩炮阻击渡江金军;金军未能突破长江,完颜亮转往瓜洲后被部下杀死,南侵失败。' },
narration: { language: 'zh-CN', estimatedSeconds: 85, fullText: '正隆六年,金海陵王完颜亮大举南下,希望渡过长江直接消灭南宋。金军抵达和州,采石江面对岸的宋军却正值主将更替,部队分散、军心不稳。中书舍人虞允文本来只是到前线犒师,并非这支军队的正式统帅。他没有等待新主将到任,而是召集将领、整合水军,把车船和中小战船布置在江面。金军从北岸放船渡江,宋军利用熟悉水势的战船机动,以弩矢、拍杆和霹雳炮等武器打乱渡船。这里的“炮”主要是投射和早期火药武器,不能画成明清火炮齐射。金军多次渡江未成,被迫离开采石转向瓜洲。完颜亮继续催促渡江,军中矛盾激化,不久在瓜洲附近被部下杀死。采石之战没有歼灭金军主力,却守住了最危险的渡口,也让金军速灭南宋的计划破产。虞允文的意义,正在于他把一支等待命令的江防军临时变成了能够作战的整体。' },
scenes: [
{ id: '01', title: '海陵南下·江防待将', timePlace: '和州北岸与采石南岸', purpose: '建立金军压境和宋军主将未至的危急状态。', narration: '完颜亮率金军抵达和州准备渡江。采石宋军主将未到,虞允文在犒师途中接手整顿。', overlay: { heading: '渡口只差一道命令', line: '金军已经下水,宋军的问题不是没有船,而是谁来让这些船同时行动。' }, people: [person('完颜亮', '金海陵王、皇帝', '督军至和州并催促渡江'), person('虞允文', '南宋中书舍人、参谋军事', '犒师至采石后临时整军'), person('采石宋军', '江防水军与步军', '在主将更替时分散待命')], image: image('01-wanyan-liang-faces-disordered-caishi-defense', '冬日长江北岸,完颜亮在金军营地俯瞰正在准备的小型渡船;南岸采石码头的宋军战船分散停泊,虞允文站在无字军册旁召集水军将领,江面宽阔。', 'Winter on the Yangtze. From the north-bank Jin camp, Wanyan Liang watches practical crossing boats being prepared. Across the broad river at Caishi, Song paddle craft and smaller warships lie dispersed until Yu Yunwen gathers officers around blank rolls at the wharf. Show the crisis of command across real distance, no magical split screen, emperor on a golden throne, modern navy or battle already underway.', '两岸同框、船只状态与虞允文召将为视觉重构;完颜亮至和州准备渡江、虞允文犒师整军有史载。'), sources: ['《宋史·虞允文传》卷三百八十三', '《金史·海陵本纪》卷五'] },
{ id: '02', title: '车船出击·渡舟受阻', timePlace: '采石矶长江水面', purpose: '呈现宋军水战机动和早期武器,但避免明清炮战误画。', narration: '金军渡船离岸,宋军车船迎击,以弩矢、拍杆和早期火药投射物打乱渡江队形。', overlay: { heading: '江面开始移动', line: '南岸船队一旦组成阵线,北岸准备好的渡船就不再面对一条空江。' }, people: [person('虞允文', '采石临时指挥者', '在岸上统筹各队与增援'), person('宋军水手', '车船桨手、弩手与甲士', '操纵战船截击渡舟'), person('金军渡江部队', '步骑与船工', '乘小船尝试建立南岸滩头')], image: image('02-song-paddle-ships-stop-jin-crossing-at-caishi', '采石宽阔江面,宋军木质车船以侧轮和桨推进,弩手从甲板射击,少量陶壳霹雳炮在远处水面产生低矮烟雾;金军小型渡船转向退避,没有金属大炮或巨爆。', 'On the broad river at Caishi, wooden Song paddle-wheel craft maneuver under oar and side-wheel power while deck crossbowmen fire. A few small early gunpowder projectiles create low smoke near the water, not giant explosions. Jin crossing boats turn and lose formation before reaching the south-bank beach. No metal cannon, broadside gunports, rockets, modern explosions, burning fleet, drowning bodies or Ming war junks.', '车船结构、弩战距离与低烟效果为视觉推演;宋车船机动、弩炮阻击金军渡江有史载,并明确不画后世火炮。'), sources: ['《宋史·虞允文传》卷三百八十三', '《续资治通鉴》绍兴三十一年条'] },
{ id: '03', title: '采石未渡·瓜洲兵变', timePlace: '采石北岸撤营与瓜洲军营', purpose: '区分采石受挫与完颜亮后来在瓜洲被杀。', narration: '金军未能从采石突破,转往瓜洲。完颜亮继续逼迫渡江,随后在军中兵变中被杀,南侵结束。', overlay: { heading: '没过江的皇帝', line: '采石挡住的是渡口;真正结束南侵的,是此后在瓜洲爆发的军心崩塌。' }, people: [person('完颜亮', '金海陵王', '离开采石转至瓜洲,后在兵变中被杀;不展示死亡'), person('金军将领', '南征诸军军官', '因强令渡江与北方变局转而兵变'), person('虞允文', '南宋官员', '守住采石后继续整备江防')], image: image('03-jin-withdraws-from-caishi-before-guazhou-mutiny', '阴夜金军瓜洲营地,完颜亮空置的主帐外军官们压低声音围着无字军令木案,远处撤离采石的渡船被拖上岸;江南岸宋军烽火仍亮,不画刺杀或尸体。', 'At a dark Jin camp near Guazhou after leaving Caishi, officers confer tensely around blank orders outside Wanyan Liang’s closed command tent. Crossing boats withdrawn from Caishi are dragged onto the bank; Song watch fires remain visible across the river. Foreshadow the later mutiny without showing assassination, a corpse or a triumphant Yu Yunwen present in the Jin camp. Keep the two events distinct.', '闭帐、军官会议与拖船为后续事件的视觉转译;金军转瓜洲、完颜亮后为部下所杀及南侵结束有史载。'), sources: ['《金史·海陵本纪》卷五', '《宋史·高宗本纪》卷三十二'] },
],
bgm: { catalogId: 'zhanzhen:caishizhizhan#0', title: '采石之战', version: '电影级配乐·专属器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '以水轮、桨和低鼓建立调度,渡江受阻形成唯一峰值;瓜洲段抽空为军帐低频,不使用近代炮声;删除人声、歌词和品牌尾签。', reason: '项目已有采石之战专属电影级配乐,临时整军、水战阻渡和后续兵变结构一致;仓库暂无成品音频。', cues: [cue('01','00:00','00:28','分散船只逐步合成节奏。'), cue('02','00:28','00:57','水轮与弩击形成唯一峰值。'), cue('03','00:57','01:25','撤舟后抽空为军帐低频。')] },
sources: [{ claim: '虞允文犒师整军、车船拒渡与采石胜负', citation: '《宋史·虞允文传》卷三百八十三', url: 'https://zh.wikisource.org/zh-hans/宋史/卷383' }, { claim: '海陵王南伐、转军瓜洲与兵变身亡', citation: '《金史·海陵本纪》卷五', url: 'https://zh.wikisource.org/zh-hans/金史/卷005' }, { claim: '采石、瓜洲战事的宋方帝纪', citation: '《宋史·高宗本纪》卷三十二', url: 'https://zh.wikisource.org/zh-hans/宋史/卷032' }],
blacklist: ['把采石画成明清金属火炮舰队对轰', '把虞允文画成原任主帅或单人驾驶战船冲阵', '把完颜亮在瓜洲被杀画成采石战场当场阵亡', '把金军所有渡船烧光或夸大为全军覆没', '现代文字、水印、明清船型服甲、现代火器、尸堆与血腥'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((item) => `${item.name}(${item.office};${item.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((item) => `| ${item.from}–${item.to} | ${item.scene} | ${item.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Southern Song, Western Xia and Jin core battle packages`)
+366
View File
@@ -0,0 +1,366 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outputRoot = path.join(repoRoot, '战役素材', '秦')
const VISUAL_BASE = `Style/medium: premium live-action Chinese historical film still, archaeology-inspired late-Qin realism, subtle film grain, physically plausible human and terrain scale. Materials: rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered lamellar armor, wooden carts, bronze and iron tools; Qin equipment uses black, dark umber and restrained cinnabar, never fantasy all-black armor. Composition: cinematic 16:9 landscape with clear foreground, middle ground and background; leave a quiet area of sky, earth, wall or water for later webpage overlay; use natural faces and distinct silhouettes. Constraints: no baked-in text, readable banners, names, subtitles, logo or watermark; violence through movement, damaged equipment and consequences, never gore. Avoid: terracotta statues as living soldiers, clone-face armies, Han/Tang/Song/Ming/Qing armor or architecture, imperial yellow dragon robes, bead-curtain crowns, Forbidden City, modern maps, UI arrows, firearms, cannons, explosions, samurai armor, European plate armor, fantasy creatures, magic or anime.`
function image(file, alt, request, visualInference) {
return {
original: `images/${file}.png`,
delivery: `images/${file}.webp`,
alt,
prompt: `Use case: historical-scene. Asset type: Digital Me late-Qin battle story frame, 16:9 landscape. Primary request: ${request} ${VISUAL_BASE}`,
visualInference,
refinementPrompt: ''
}
}
const packs = [
{
dir: '蒙恬北击匈奴(略取河南地)',
event: {
canonicalName: '蒙恬北击匈奴(略取河南地)',
aliases: ['蒙恬北击胡', '蒙恬略取河南地', '秦北击匈奴'],
era: '秦始皇三十二年',
date: '公元前215年',
place: '秦帝国北部边地与河南地(今河套一带)',
summary: '秦始皇因“亡秦者胡也”命蒙恬发兵三十万北击匈奴,秦军略取河南地。'
},
narration: {
language: 'zh-CN', estimatedSeconds: 76,
fullText: '公元前二一五年,燕人卢生从海上归来,献上一句“亡秦者胡也”。秦始皇把“胡”理解为北方匈奴,于是命蒙恬发兵三十万北上。对普通戍卒而言,他们未必听过那句谶语,只知道军令已下,要离开关中,穿过干旱的北地。蒙恬率军向河南地推进,把匈奴势力逐出黄河河套以南地区。《史记》只留下“北击胡,略取河南地”九个字,没有记录一场可以被命名的决战,也没有给出双方伤亡。真正改变边疆的,是大军、粮车和戍守体系持续向北移动。秦在北线赢得了土地,却也把三十万人的劳役与补给压在帝国最远的边缘。'
},
scenes: [
{
id: '01', title: '谶语入宫', timePlace: '公元前215年,秦都咸阳', purpose: '交代北征决策的史书记载,不把谶语表现成神迹。',
narration: '卢生献上“亡秦者胡也”。始皇把“胡”理解为匈奴,北征由此发端。',
overlay: { heading: '谶语入宫', line: '一句含混的话,被读成了三十万人的军令。' },
people: [
{ name: '秦始皇', office: '皇帝', action: '听取图书之语,决定北击匈奴' },
{ name: '卢生', office: '燕人方士', action: '奏录图书“亡秦者胡也”' }
],
image: image('01-prophecy-at-court', '咸阳一座克制的秦代殿室内,卢生呈上一卷竹简,秦始皇与群臣审视,窗外是冷灰天光,没有发光文字或神异景象。', 'Interior of a restrained late-Qin audience hall in Xianyang. Lu Sheng, a travel-worn middle-aged East Asian man in plain hemp and dark robe, presents a closed bamboo-slip bundle with both hands; no writing is visible. Qin Shi Huang, mature East Asian ruler in austere black-brown court robes and a simple period-appropriate headpiece, studies him from a low raised platform while a few officials stand apart. Cold daylight enters from one side, leaving calm negative space on a rammed-earth wall; political unease, not supernatural spectacle.', '人物站位、殿室形制、竹简封闭状态与冷光为视觉重构;史料只载卢生奏图书及其语。'),
sources: ['《史记·秦始皇本纪》卷六']
},
{
id: '02', title: '三十万北上', timePlace: '秦始皇三十二年,关中通往北地的道路', purpose: '把“发兵三十万”落到征发、运输与普通士卒身上。',
narration: '蒙恬奉命发兵三十万。人马与粮车沿北地道路拉成长线,军令离宫廷越来越远。',
overlay: { heading: '三十万北上', line: '史书写的是数字,路上走的是一个个具体的人。' },
people: [
{ name: '蒙恬', office: '秦将军', action: '统率北征军' },
{ name: '秦军戍卒', office: '征发士卒', action: '随军北上并承担行军、护粮与戍守' }
],
image: image('02-army-marches-north', '黄土高地的长坡上,蒙恬在前景观察北上队伍,秦军步卒、骑兵与粮车分层延伸到远处,人数庞大但比例真实。', 'Vast but physically plausible northbound Qin column crossing a dry loess ridge. Meng Tian, a weathered East Asian commander in his forties with tied hair, short moustache, dark lacquered lamellar armor and plain brown-black cloak, watches from a foreground rise. Infantry files, modest cavalry, pack animals and wooden grain carts follow separate tracks into the middle distance; emphasize logistics and fatigue rather than a parade. Pale open sky provides overlay space.', '具体路线、队列编组、蒙恬外貌与粮车层次为视觉补全;“三十万”与蒙恬统兵有史载。'),
sources: ['《史记·秦始皇本纪》卷六']
},
{
id: '03', title: '北击于河', timePlace: '河南地南缘,黄河沿岸', purpose: '呈现北击行动,但不虚构某场有名称、有精确阵形的决战。',
narration: '北线没有留下单一决战的名字。秦军沿河推进,迫使匈奴骑队向北撤离。',
overlay: { heading: '北击于河', line: '不是一场被命名的大会战,而是一条战线持续北移。' },
people: [
{ name: '蒙恬', office: '秦将军', action: '指挥秦军沿河南地推进' },
{ name: '匈奴骑队', office: '北方游牧武装', action: '在秦军推进压力下向北转移;具体首领不明' }
],
image: image('03-drive-across-the-river-loop', '黄河弯道旁的开阔草原上,秦军盾弩阵稳步推进,远处匈奴骑队转向北方,蒙恬从侧后方观察,不表现虚构单挑。', 'Wide Yellow River bend and open grassland at the southern edge of the Ordos loop. Qin crossbowmen and shield infantry advance in disciplined, human-scale groups while a distant Xiongnu mounted formation turns away across higher ground, preserving mobility rather than charging theatrically. Meng Tian, matching scene 02, observes from the flank beside aides. Dust, cold wind, no named duel, no giant clash, no readable standards.', '黄河视角、兵种配置、骑队撤离方向与同框关系为合理视觉推演;史料仅载北击胡、略取河南地。'),
sources: ['《史记·秦始皇本纪》卷六']
},
{
id: '04', title: '河南地易手', timePlace: '战后,河南地', purpose: '以驻防与补给收束“略取河南地”,提示边功伴随长期成本。',
narration: '秦军略取河南地。新营垒和粮道留在河套,而北征的代价也从战场转成了长期戍守。',
overlay: { heading: '河南地易手', line: '土地向北扩了一段,补给线也被拉长了一段。' },
people: [
{ name: '蒙恬', office: '秦将军', action: '主持新占地区的军队部署' },
{ name: '秦军戍卒与民夫', office: '边地驻防及运输人员', action: '修整营垒、转运粮草;具体编制不明' }
],
image: image('04-henan-land-held', '河套暮色中,一座朴素秦军营垒正在搭建,戍卒修补栅栏、民夫卸下粮袋,蒙恬远望黄河与北方草地,没有凯旋仪式。', 'Quiet dusk after the campaign in the Henan lands. A modest Qin frontier encampment of timber palisades, earthworks and low tents takes shape beside the river plain. Soldiers repair defenses while laborers unload grain sacks from wooden carts; tired horses drink nearby. Meng Tian, same identity, stands on an earth bank facing the northern grassland, with no triumphal pose. Warm last light under a large subdued sky, the cost of holding territory visible in routine work.', '营垒、粮运、暮色与蒙恬远望为视觉补全;“略取河南地”是史实,具体驻防画面不作制度细节断言。'),
sources: ['《史记·秦始皇本纪》卷六']
}
],
bgm: {
catalogId: 'zhanzhen:baideng#0', title: '白登之围', version: '电影级配乐·仅取无主唱草原器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '保留低沉草原长音、风声质感与稀疏鼓点,删除全部歌词、人物主题和“白登”专属声音;第三幕略加强节奏,结尾回到补给与戍守的冷静。',
reason: '项目尚无蒙恬北征专属曲;此曲属于项目自生成的北方草原战事音乐,抽取无主唱器乐层后可复用,仓库内暂无成品音频。',
cues: [
{ from: '00:00', to: '00:19', scene: '01', direction: '极低配器,避免把谶语神秘化。' },
{ from: '00:19', to: '00:38', scene: '02', direction: '加入行军脉冲,突出漫长队列与补给。' },
{ from: '00:38', to: '00:57', scene: '03', direction: '短促加强节奏,不做英雄式爆点。' },
{ from: '00:57', to: '01:16', scene: '04', direction: '撤去主鼓,仅留低弦、风感和稀疏金石音。' }
]
},
sources: [
{ claim: '卢生奏“亡秦者胡也”、始皇命蒙恬发兵三十万北击胡并略取河南地', citation: '《史记·秦始皇本纪》卷六', url: 'https://zh.wikisource.org/zh-hans/史记/卷006' }
],
blacklist: [
'把谶语画成发光神书、天降文字或确定预言;它只是史书记载中的政治触发点',
'虚构一场有名称、精确日期、精确伤亡或单挑过程的“河南地决战”',
'把秦军三十万理解为单个镜头必须出现三十万人,或给匈奴编造对等精确兵力',
'把“河南地”误解为今天河南省;此处指黄河河套以南地区',
'现代文字、字幕、书法、地图箭头、Logo、水印或可读军旗',
'秦兵陶俑化、克隆脸、玄幻黑甲、后世盔甲建筑、火器爆炸或血腥尸山'
]
},
{
dir: '章邯骊山徒破周章',
event: {
canonicalName: '章邯骊山徒破周章',
aliases: ['章邯赦骊山徒', '戏之战', '章邯破周章'],
era: '秦二世二年冬',
date: '公元前208年',
place: '戏及咸阳东部、骊山一带',
summary: '周章军西至戏、逼近咸阳;少府章邯请赦骊山刑徒并授兵,率临时编成的秦军击破周章。'
},
narration: {
language: 'zh-CN', estimatedSeconds: 79,
fullText: '公元前二〇八年冬,陈胜派出的周章一路向西,军队已到戏,逼近秦都咸阳。《史记》说其兵数十万,秦二世大惊,问群臣怎么办,朝堂一时无人能答。少府章邯站了出来。他原本掌管宫廷财政与官府手工业,不是统兵将领,却看见了咸阳眼前唯一还来得及动员的人:骊山刑徒。章邯请求赦免这些正在服役的囚徒,发给兵器,让他们立刻迎敌。二世大赦,使章邯统领这支临时军队。昨天还拿着土筐和工具的人,今天被编入军阵。史书没有留下戏地交战的阵形和伤亡,只记章邯击破周章,迫使其败走。这场胜利暂时解除了咸阳之危,也暴露了秦帝国已经必须靠刑徒组成最后的野战军。'
},
scenes: [
{
id: '01', title: '周章兵至戏', timePlace: '公元前208年冬,戏与咸阳以东', purpose: '建立周章军逼近秦都的速度与压力,不把“数十万”当作精确点数。',
narration: '周章军西入至戏,距离咸阳已经不远。秦廷第一次直接听见关东起义军逼近都城。',
overlay: { heading: '周章兵至戏', line: '关东的火,已经烧到咸阳门外。' },
people: [
{ name: '周章', office: '陈胜所遣将领', action: '率起义军西进至戏' },
{ name: '周章军', office: '反秦军', action: '逼近咸阳;史载“兵数十万”' }
],
image: image('01-zhou-zhang-reaches-xi', '冬日黄土原上,周章站在行军队伍前观察通往咸阳的道路,反秦军由不同衣着和简陋装备的人组成,远处可见关中驿路烟尘。', 'Winter loess plain near Xi east of Xianyang. Zhou Zhang, a sturdy East Asian rebel commander in his forties wearing worn mixed armor and a dark cloth cloak, surveys the westward road from a low ridge. Behind him stretches a large, uneven rebel host: farmers, former conscripts and armed followers with mismatched shields, spears and supply carts, visibly formidable but not an impossible digital swarm. Distant road dust points toward the capital; no city skyline fantasy, no readable banners.', '周章外貌、军队成分、视野与行军队列为视觉重构;西至戏及“兵数十万”见史载,人数不作精确可视化。'),
sources: ['《史记·秦始皇本纪》卷六']
},
{
id: '02', title: '少府献策', timePlace: '秦都咸阳,秦二世朝廷', purpose: '突出章邯由少府转为领兵者的关键提议。',
narration: '二世问群臣“奈何”。少府章邯答:近县征兵已来不及,骊山刑徒很多,请赦免并授兵。',
overlay: { heading: '少府献策', line: '常规征兵来不及,唯一现成的人力在骊山。' },
people: [
{ name: '秦二世', office: '皇帝', action: '惊问群臣并接受章邯建议' },
{ name: '章邯', office: '少府', action: '请赦骊山徒、授兵迎敌' },
{ name: '秦廷群臣', office: '公卿', action: '面对周章逼近未能提出对策' }
],
image: image('02-zhang-han-proposes-pardon', '昏暗秦廷内,章邯从沉默群臣中上前陈策,秦二世坐在上方急切注视,地上展开的只是无字木牍与军报,不出现后世地图。', 'Tense late-Qin court in Xianyang. Zhang Han, an alert middle-aged East Asian official in restrained dark-brown administrative robes rather than general armor, steps forward from a line of silent ministers and addresses Qin Er Shi. The young emperor sits rigidly on a modest raised platform in austere robes, alarm visible without caricature. An attendant holds sealed wooden reports with no visible writing; empty rammed-earth wall and shadow dominate the frame. Political emergency, no ornate Forbidden City grandeur.', '人物神态、朝堂空间与军报道具为视觉补全;二世问计、群臣无对、章邯请赦授兵有史载。'),
sources: ['《史记·秦始皇本纪》卷六']
},
{
id: '03', title: '刑徒授兵', timePlace: '骊山役所', purpose: '把大赦与临时编军呈现为制度性转换,不浪漫化刑徒处境。',
narration: '赦令下达。骊山刑徒放下土筐和工具,领取盾矛,在秦吏监督下匆忙编伍。',
overlay: { heading: '刑徒授兵', line: '赦免不是回家,而是把工具换成兵器。' },
people: [
{ name: '章邯', office: '少府、临时领军者', action: '组织刑徒授兵编伍' },
{ name: '骊山刑徒', office: '服役囚徒', action: '获赦后被授兵迎战周章' },
{ name: '秦吏', office: '役所与军队管理人员', action: '核验并组织发放兵器;具体流程为视觉重构' }
],
image: image('03-convicts-receive-arms', '骊山脚下的役所空地上,疲惫刑徒排队放下土筐、领取盾和矛,章邯已换上实用甲胄在一旁组织,陵园只以远处土木工地暗示。', 'Open work yard at the foot of Lishan. Exhausted adult convicts in worn hemp clothing set down earth baskets, wooden shovels and carrying poles, then receive practical shields and spears from Qin storekeepers; show uncertainty, fatigue and coercion rather than celebratory enlistment. Zhang Han now wears simple functional lacquered armor over official clothing and organizes rough files with a few officers. The imperial tomb works are only distant earthworks and scaffolds, never a fantasy mausoleum or visible terracotta army.', '授兵流程、刑徒衣着、章邯换甲及工地形态为视觉补全;赦骊山徒、授兵、使章邯将有史载。'),
sources: ['《史记·秦始皇本纪》卷六']
},
{
id: '04', title: '破军而走', timePlace: '戏附近,交战后段', purpose: '忠实呈现“击破周章军而走”,避免编造战术细节与夸张屠杀。',
narration: '章邯率临时编成的秦军迎战。周章军被击破向东败走,咸阳的危机暂时解除。',
overlay: { heading: '破军而走', line: '秦赢回了时间,也暴露了自己还能动用什么。' },
people: [
{ name: '章邯', office: '秦军将领', action: '率骊山徒组成的军队击破周章' },
{ name: '周章', office: '反秦将领', action: '军败后撤;本幕不延伸其后续结局' },
{ name: '骊山徒军', office: '秦军临时兵员', action: '参与交战并取得胜势' }
],
image: image('04-rebels-retreat-from-xi', '戏附近尘土弥漫的战场上,章邯指挥刚编成的盾矛军稳住阵线,周章军向远处有序转为溃退,地面有丢弃器械但不见尸山。', 'Late phase of fighting near Xi on a dusty winter plain. Zhang Han, matching scene 03, directs a rough but holding Qin shield-and-spear line made from newly armed convicts. In the middle distance Zhou Zhang, matching scene 01, turns his fragmented forces eastward into retreat; broken carts, dropped shields and snapped spear shafts show collapse without gore. Avoid depicting a specific undocumented trick, formation or duel. Wide oblique view, capital-safe relief mixed with exhaustion, quiet sky for overlay.', '阵线形态、撤退方向、交战器械与两将同框为视觉推演;史料只明确章邯击破周章军而走。'),
sources: ['《史记·秦始皇本纪》卷六']
}
],
bgm: {
catalogId: 'zhanzhen:julu#1', title: '巨鹿之战', version: '王离版·仅取无主唱器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '只复用下沉的大提琴、低频与稀疏打击层,删除王离歌词与巨鹿专属声场;第二幕短暂收空,第三幕加入粗粝节奏,结尾不作胜利凯歌。',
reason: '项目尚无周章之战专属曲;王离版属于项目自生成的秦末军团主题,其下沉质感能连接“临时续命”与后续巨鹿崩解,仓库内暂无成品音频。',
cues: [
{ from: '00:00', to: '00:20', scene: '01', direction: '低频压迫建立逼都危机。' },
{ from: '00:20', to: '00:40', scene: '02', direction: '撤去鼓点,以一条低弦托住朝堂问答。' },
{ from: '00:40', to: '01:00', scene: '03', direction: '加入木质敲击和不整齐行军节拍。' },
{ from: '01:00', to: '01:19', scene: '04', direction: '短促加强后迅速下坠,提示这只是暂时解围。' }
]
},
sources: [
{ claim: '周章西至戏、二世问计、章邯请赦骊山徒授兵并击破周章', citation: '《史记·秦始皇本纪》卷六', url: 'https://zh.wikisource.org/zh-hans/史记/卷006' },
{ claim: '周章为陈胜所遣将领及其西进背景', citation: '《史记·陈涉世家》卷四十八', url: 'https://zh.wikisource.org/zh-hans/史记/卷048' }
],
blacklist: [
'把少府章邯画成一开始就是世袭大将;他以少府身份献策并由此领军',
'把骊山刑徒浪漫化为自愿效忠、整齐精锐,或画成秦始皇陵兵马俑复活',
'虚构戏地精确阵形、伏击计谋、单挑、双方伤亡和一日内完整战斗过程',
'把周章在后续战败后的结局直接画进本次“破军而走”现场',
'现代文字、字幕、书法、地图箭头、Logo、水印或可读军旗',
'后世盔甲建筑、龙袍冕旒、火器爆炸、玄幻法术、克隆脸或血腥尸山'
]
},
{
dir: '巨鹿之战(破釜沉舟)',
event: {
canonicalName: '巨鹿之战(破釜沉舟)',
aliases: ['钜鹿之战', '破釜沉舟', '项羽救赵'],
era: '秦二世三年',
date: '公元前207年',
place: '钜鹿城、棘原及邻近河道(古城位置约今河北邢台平乡一带,尚有异说)',
summary: '项羽夺取楚军指挥权后渡河决战,九战绝秦军甬道,击溃围城的王离军,杀苏角、俘王离,解除钜鹿之围并成为诸侯上将军。'
},
narration: {
language: 'zh-CN', estimatedSeconds: 89,
fullText: '公元前二〇七年,秦将王离围困钜鹿,赵王歇和张耳困在城中,粮食将尽。章邯的大军驻在钜鹿以南的棘原,修筑甬道,把粮食送给围城军。十余路诸侯来救,却都停在营垒里观望。楚军上将宋义在安阳停留四十六日,项羽杀宋义,取得指挥权。他先遣英布、蒲将军率二万人渡河,随后率全军渡河,沉船、破釜甑、烧庐舍,只带三日粮。决战中,楚军九次交锋,切断秦军甬道,使王离军失去补给。苏角战死,王离被俘,涉间不降而自尽。观战的诸侯将领进入项羽辕门时,无不跪行,不敢仰视。钜鹿之围解除,项羽由此成为诸侯上将军。后来章邯投降与新安坑降卒,是这场战役的后续,不应混在同一天的钜鹿战场。'
},
scenes: [
{
id: '01', title: '孤城与甬道', timePlace: '钜鹿城与城南棘原', purpose: '解释秦军围城体系:王离攻城,章邯驻南并以甬道输粮。',
narration: '王离急攻钜鹿,城中食尽兵少。章邯驻军城南棘原,以夹墙甬道向围城军输粮。',
overlay: { heading: '孤城与甬道', line: '围城靠的不只兵力,还有一条不断粮的通道。' },
people: [
{ name: '王离', office: '秦武城侯、围城前线主将', action: '率长城戍边军围攻钜鹿' },
{ name: '章邯', office: '秦军全局主帅', action: '驻棘原并筑甬道输粮' },
{ name: '赵王歇、张耳', office: '赵王与赵相', action: '被围城中,食尽兵少' }
],
image: image('01-city-and-supply-corridor', '高处俯瞰钜鹿夯土城、王离围城营垒与通向南方棘原的夹墙甬道,粮车正在通道中运行,三层空间关系清楚但不像现代地图。', 'Elevated oblique view across the Julu battlefield on the Hebei plain. A compact rammed-earth city with worn timber gates sits under tight Qin siege lines led by Wang Li; southward, a long protected supply corridor formed by parallel low earth walls guides grain carts toward the siege camps, visually linking to Zhang Han’s distant field encampment at Jiyuan. Inside the city, sparse defenders are visible without individual close-ups. Make geography legible through natural terrain and depth, never as a modern map or infographic.', '古城、营垒、甬道和棘原军营的同框空间关系为视觉重构;王离围城、章邯军棘原及筑甬道输粟有史载。'),
sources: ['《史记·项羽本纪》卷七', '《史记·张耳陈余列传》卷八十九']
},
{
id: '02', title: '斩宋义夺军', timePlace: '安阳,楚军停驻期间', purpose: '交代项羽如何从次将成为实际主帅;克制表现帐中处决。',
narration: '宋义在安阳停留四十六日。项羽入帐杀宋义,楚怀王随后任命项羽为上将军。',
overlay: { heading: '斩宋义夺军', line: '救赵之前,楚军先换了主帅。' },
people: [
{ name: '项羽', office: '楚军次将,后为上将军', action: '帐中杀宋义并取得救赵军指挥权' },
{ name: '宋义', office: '楚上将军、卿子冠军', action: '停军安阳四十六日,后被项羽所杀' },
{ name: '楚军将领', office: '救赵军军官', action: '在权力骤变后接受项羽指挥' }
],
image: image('02-command-changes-at-anyang', '寒雨后的楚军营帐内,项羽持未出鞘佩剑站在倒下的案几旁,宋义已不在正面视野,诸将震惊而沉默,画面以权力更替而非斩首血腥为中心。', 'Inside a rain-darkened Chu command tent at Anyang after the decisive confrontation. Xiang Yu, a powerful young East Asian commander in his mid-twenties with broad shoulders, tied hair, weathered face and practical dark Chu lamellar armor with muted red-brown cloth, stands beside an overturned command table, one hand on a sheathed sword. Song Yi is implied only by an abandoned official cap and screened attendants at the far edge, no severed head or gore. Shocked Chu officers hold still as authority shifts. Cold wet camp visible through the open flap; tense, contained political violence.', '帐内位置、倒案、佩剑和以冠帽暗示宋义结局均为克制视觉重构;停安阳四十六日、项羽帐中斩宋义及继任有史载。'),
sources: ['《史记·项羽本纪》卷七']
},
{
id: '03', title: '渡河破釜', timePlace: '钜鹿外围河道,决战前', purpose: '呈现破釜沉舟的完整行动,不把它简化成只砸一口锅的摆拍。',
narration: '英布、蒲将军先率二万人渡河。项羽随后引全军渡河,沉船、破釜、烧庐,只带三日粮。',
overlay: { heading: '渡河破釜', line: '船沉、釜破、庐烧,退路被主动从军中拿走。' },
people: [
{ name: '项羽', office: '楚上将军', action: '率楚军主力渡河并下达绝退行动' },
{ name: '英布', office: '当阳君、楚军先遣将领', action: '与蒲将军率二万先渡河击秦' },
{ name: '蒲将军', office: '楚军先遣将领', action: '与英布率二万先渡河' }
],
image: image('03-cross-river-break-cauldrons', '河滩上楚军完成渡河,士卒凿沉木船、砸破陶釜并拆烧临时庐舍,项羽在高处监督向前集结,动作真实且无夸张火海。', 'Broad riverbank after the Chu crossing. Soldiers deliberately hole and push simple wooden boats into the water, smash clay cooking cauldrons with stones, and burn a few rough shelters in controlled low flames; others shoulder only small three-day ration bundles and form for the advance. Xiang Yu, same identity as scene 02, stands on a low bank directing the forward assembly. Show purposeful discipline and unease, not a festival bonfire. No readable banners, no huge explosion, no modern bridge.', '具体河岸、船型、破釜动作、三日粮包与火势为视觉补全;沉船、破釜甑、烧庐舍、持三日粮有史载。'),
sources: ['《史记·项羽本纪》卷七']
},
{
id: '04', title: '九战绝甬道', timePlace: '钜鹿城外及秦军甬道', purpose: '把胜负关键落在连续交战与切断补给,不虚构单次冲锋即结束全役。',
narration: '楚军与秦军九战,反复截断甬道。粮车停住,王离围城军逐渐失去补给,攻守由此逆转。',
overlay: { heading: '九战绝甬道', line: '真正被击中的,是围城军赖以维持的粮道。' },
people: [
{ name: '项羽', office: '楚上将军', action: '率军九战并绝秦军甬道' },
{ name: '王离', office: '秦军前线主将', action: '围城军在粮道被断后陷入乏食' },
{ name: '楚军与秦军士卒', office: '交战双方', action: '围绕甬道反复争夺' }
],
image: image('04-nine-battles-cut-the-corridor', '破损甬道旁,楚军越过倒塌土墙截住粮车,项羽指向前方组织推进;王离的秦军在远处回防,连续争夺通过尘土与多处旧损表现。', 'Decisive fighting around the Qin supply corridor. Sections of the parallel earth walls are breached; Chu spear-and-shield groups surge through to halt grain carts while Qin defenders pivot from the siege lines to protect the route. Xiang Yu, same face and armor, directs from the active middle ground without superhero posing. Repeated prior clashes are suggested by repaired walls, several old breaks, abandoned cart wheels and trampled earth, not nine simultaneous cloned battles. Dynamic wide frame, non-graphic action, Julu city distant in haze.', '九次交战以多处新旧破损压缩表现,具体突破点、粮车位置与项羽指挥姿态为视觉推演;“九战,绝其甬道”有史载。'),
sources: ['《史记·项羽本纪》卷七', '《史记·张耳陈余列传》卷八十九']
},
{
id: '05', title: '王离被俘', timePlace: '钜鹿城外,秦围城军崩溃后', purpose: '呈现围城解除与项羽成为诸侯领袖,不把章邯后降和新安坑卒混入。',
narration: '苏角战死,王离被俘,涉间不降自尽。诸侯将入项羽辕门跪行,钜鹿之围终于解除。',
overlay: { heading: '王离被俘', line: '一场解围战,把项羽推成了诸侯上将军。' },
people: [
{ name: '项羽', office: '楚上将军', action: '大破围城秦军,成为诸侯上将军' },
{ name: '王离', office: '秦武城侯、围城前线主将', action: '兵败被俘' },
{ name: '苏角、涉间', office: '秦将', action: '苏角战死;涉间不降自尽,均不作血腥近景' },
{ name: '诸侯将领', office: '各路援军主将', action: '入辕门膝行,不敢仰视' }
],
image: image('05-wang-li-captured', '楚军辕门前,王离卸下兵器被看守但未受羞辱,项羽站在营内接受战报,数名诸侯将领低身进入;远处钜鹿城门开启,守军与百姓谨慎走出。', 'After the collapse of the Qin siege army, outside the Chu command gate. Wang Li, an older East Asian Qin commander in damaged but dignified dark lamellar armor, has surrendered his sword and stands guarded, not bound, kneeling or humiliated. Xiang Yu, consistent with prior scenes, receives reports inside the gate while several allied lords enter deeply bowed and hesitant, conveying “dare not look up” without a mass worship spectacle. Far behind, Julu’s gate opens and thin defenders emerge. Quiet aftermath, scattered weapons, no portrayal of Su Jiao’s corpse or She Jian’s death.', '王离交剑、诸侯人数与姿态、城门开启及解围后的同框为视觉补全;杀苏角、虏王离、涉间不降自尽、诸侯膝行与围解均有史载。'),
sources: ['《史记·项羽本纪》卷七', '《史记·张耳陈余列传》卷八十九']
}
],
bgm: {
catalogId: 'zhanzhen:julu#0', title: '巨鹿之战', version: '电影级配乐·建议使用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '抽取项目专属巨鹿曲的器乐层:第一幕以压迫低频入场,第二幕收紧,第三幕建立连续推进,第四幕达到唯一高潮,第五幕撤鼓留余震;删除全部歌词与品牌尾签。',
reason: '项目内与巨鹿直接绑定的自生成音乐方案,情绪结构与破釜、绝甬道、诸侯归服的五幕推进完全匹配;仓库内暂无可直接附加的成品音频。',
cues: [
{ from: '00:00', to: '00:18', scene: '01', direction: '低频与孤立敲击建立围城和粮道压力。' },
{ from: '00:18', to: '00:36', scene: '02', direction: '收窄声场,以低弦紧张表现军权骤变。' },
{ from: '00:36', to: '00:54', scene: '03', direction: '行军鼓逐层加入,但不在破釜动作处提前爆发。' },
{ from: '00:54', to: '01:12', scene: '04', direction: '全曲唯一高潮落在甬道被截断。' },
{ from: '01:12', to: '01:30', scene: '05', direction: '迅速撤鼓,留低弦与金石余响收束权力转移。' }
]
},
sources: [
{ claim: '宋义停军、项羽夺军、先遣二万、破釜沉舟、九战绝甬道、杀苏角虏王离及诸侯归服', citation: '《史记·项羽本纪》卷七', url: 'https://zh.wikisource.org/zh-hans/史记/卷007' },
{ claim: '钜鹿城中粮尽兵少、章邯军棘原、甬道输粮、王离军乏食与围城解除', citation: '《史记·张耳陈余列传》卷八十九', url: 'https://zh.wikisource.org/zh-hans/史记/卷089' },
{ claim: '王离为王翦之孙、王贲之子及其兵败被俘', citation: '《史记·白起王翦列传》卷七十三', url: 'https://zh.wikisource.org/zh-hans/史记/卷073' }
],
blacklist: [
'把章邯和王离混成同一位前线主将;王离围城,章邯驻棘原并负责后援粮道',
'宣称秦军总数为四十万;正史无钜鹿秦军总兵力,二十余万是后来章邯降卒数据',
'把安阳寒雨当成钜鹿决战当天的天气,或把天气写成胜负关键',
'把破釜沉舟画成项羽独自砸一口锅,忽略沉船、烧庐、持三日粮与全军行动',
'把新安坑杀二十余万降卒、章邯洹水投降直接混进钜鹿决战当天',
'把古钜鹿位置断言为今天巨鹿县唯一确定地点;项目采用平乡一带但注明有异说',
'现代文字、字幕、地图箭头、Logo、水印、可读军旗、后世盔甲建筑、火器爆炸或血腥尸山'
]
}
]
function renderMarkdown(pack) {
const lines = [
`# ${pack.event.canonicalName}|素材说明`, '',
'## 事件边界', '',
`- 规范事件名:${pack.event.canonicalName}`,
`- 常用称呼:${pack.event.aliases.join('、')}`,
`- 时代:${pack.event.era}`,
`- 日期:${pack.event.date}`,
`- 地点:${pack.event.place}`,
`- 一句话结论:${pack.event.summary}`, '',
'## 60–90 秒主旁白', '', pack.narration.fullText, '',
'## 分幕素材', ''
]
for (const scene of pack.scenes) {
lines.push(
`### ${scene.id}|${scene.title}`, '',
`- 时间地点:${scene.timePlace}`,
`- 本幕作用:${scene.purpose}`,
`- 分幕旁白:${scene.narration}`,
`- 页面标题:${scene.overlay.heading}`,
`- 页面短句:${scene.overlay.line}`,
`- PNG:\`${scene.image.original}\``,
`- WebP:\`${scene.image.delivery}\``,
`- 替代文本:${scene.image.alt}`,
`- 视觉推演边界:${scene.image.visualInference}`,
`- 人物:${scene.people.map((person) => `${person.name}(${person.office};${person.action})`).join(';')}`,
`- 史料:${scene.sources.join(';')}`, '',
'生成提示词:', '', '```text', scene.image.prompt, '```', ''
)
}
lines.push('## BGM 选曲', '',
`- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`,
`- 状态:${pack.bgm.status}`,
`- 用法:${pack.bgm.usage}`,
`- 选择理由:${pack.bgm.reason}`,
`- 音频路径或 URL:${pack.bgm.pathOrUrl || '仓库暂缺,待导出'}`, '',
'| 时间 | 分幕 | 编排 |', '|---|---|---|',
...pack.bgm.cues.map((cue) => `| ${cue.from}–${cue.to} | ${cue.scene} | ${cue.direction} |`), '',
'## 史料来源', '',
...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '',
'## 禁区与误读防护', '',
...pack.blacklist.map((item) => `- ${item}`), '',
'## 交付检查', '',
'- [ ] 每幕 PNG 原图存在且为 16:9',
'- [ ] 每幕 WebP 交付图存在且为 16:9',
'- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全',
`- [${pack.bgm.status === 'ready' ? 'x' : ' '}] BGM 已导出并填写可用路径`,
'- [ ] `manifest.json` 与本说明中的文件名全部校验通过', ''
)
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const packDir = path.join(outputRoot, pack.dir)
if (fs.existsSync(packDir)) throw new Error(`Refusing to overwrite existing package: ${packDir}`)
fs.mkdirSync(path.join(packDir, 'images'), { recursive: true })
const manifest = {
event: pack.event,
narration: pack.narration,
scenes: pack.scenes,
bgm: pack.bgm,
sources: pack.sources,
blacklist: pack.blacklist
}
fs.writeFileSync(path.join(packDir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`)
fs.writeFileSync(path.join(packDir, '素材说明.md'), renderMarkdown(pack))
}
console.log(`Generated ${packs.length} Qin gap packages under ${outputRoot}`)
@@ -0,0 +1,87 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材', '蜀汉')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed Three Kingdoms material culture, cinematic 16:9 landscape, subtle film grain, physically plausible scale, rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered iron lamellar armor, straight swords, crossbows, spears, ji halberds, cavalry tack and wooden carts. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, samurai armor, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Shu Han story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const packs = [
{
dir: '定军山之战(黄忠乘高破敌)',
event: { canonicalName: '定军山之战(黄忠乘高破敌)', aliases: ['定军山之战', '汉中之战·定军山', '黄忠斩夏侯渊'], projectDynasty: '蜀汉', era: '东汉献帝建安二十四年', date: '公元219年正月前后', place: '汉中沔水南岸、走马谷与定军山', summary: '刘备渡沔水沿山进营,以夜烧鹿角调动魏军;夏侯渊分兵救援、亲赴南围后,法正判断战机已至,黄忠乘高鼓噪突击,夏侯渊战死,魏军退守阳平。' },
narration: { language: 'zh-CN', estimatedSeconds: 88, fullText: '建安二十四年,刘备与夏侯渊争夺汉中。刘备从阳平南渡沔水,沿山逐步前进,依定军山地势立营。他先以精兵分部夜攻张郃,又焚烧魏军围鹿角和走马谷营障,迫使魏军分头应对。夏侯渊让张郃守东围,自己带轻兵守南围;张郃一面吃紧,他又把所部一半拨去增援。主帅的兵力和注意力由此被拉散。夏侯渊亲自赶往修补被焚工事时,法正从高处观察魏阵,判断机会已经成熟,对刘备说可以攻击。刘备随即命黄忠出击。黄忠率军凭借高地,金鼓齐作,向下冲击。正史只说黄忠乘高鼓噪、奋勇破敌,并没有“阵前单刀劈杀”的过程;魏书则记夏侯渊救火时与蜀军短兵接战而死。夏侯渊与赵颙战死,魏军一度失去主帅。郭淮收拢散卒,推举张郃主持诸军,才稳定阵线退守阳平。随后曹操亲自入汉中也未能扭转局势,刘备最终占有汉中,并在当年进位汉中王。' },
scenes: [
{ id: '01', title: '南渡沔水·缘山进营', timePlace: '汉中沔水南岸、定军山下', purpose: '用地形说明刘备为何逐步取得高地优势。', narration: '刘备南渡沔水,沿山逐步前进,在定军山依势立营。', overlay: { heading: '缘山进营', line: '越过沔水以后,谁先站上山势,谁就先改变了攻守。' }, people: [{ name: '刘备', office: '左将军、益州牧', action: '统军南渡沔水,缘山进至定军山' }, { name: '法正', office: '蜀郡太守、扬武将军', action: '随军观察魏军部署与山势' }], image: image('01-crossing-mian-and-taking-high-ground', '蜀军用浅底船和浮排渡过沔水,刘备法正在岸边观察部队沿山坡分层建立营垒,远处可见魏军低处营地。', 'Cool morning on the Mian River below Dingjun Mountain. Shu troops cross in shallow wooden boats and lashed rafts, then climb in separated columns to establish modest tiered earth-and-timber camps along the slope. Liu Bei, late 50s, lean face, short beard, restrained brown-black armor and cloak, studies the heights beside Fa Zheng, a thin middle-aged adviser in plain dark robes. Wei positions remain lower and distant. Emphasize terrain and gradual movement, no grand stone fortress or yellow imperial robe.', '渡河舟具、营垒层次、人物外貌与双方可视距离为视觉重构;刘备南渡沔水、缘山稍前、于定军山势作营有史载。'), sources: ['《三国志·蜀书·先主传》卷三十二', '《三国志·蜀书·法正传》卷三十七'] },
{ id: '02', title: '夜攻广石·焚围鹿角', timePlace: '走马谷与魏军东南围,夜间', purpose: '表现多点夜攻如何迫使魏军分兵。', narration: '蜀军分部夜攻张郃广石,又焚烧围鹿角。魏军不得不同时守住东围和南围。', overlay: { heading: '夜烧鹿角', line: '火没有直接烧垮大军,却把魏军主帅从完整阵线里调了出来。' }, people: [{ name: '刘备', office: '蜀军主帅', action: '分遣精卒夜攻并焚烧魏军营障' }, { name: '张郃', office: '魏荡寇将军', action: '在广石与东围抵御蜀军夜攻' }, { name: '夏侯渊', office: '魏征西将军', action: '分兵支援张郃并自守南围' }], image: image('02-night-attacks-and-burning-abatis', '定军山夜色中,蜀军十余支小队在不同山谷方向压向魏营,几处木制鹿角燃烧;张郃守东围,夏侯渊从另一侧调兵,不画火烧人群。', 'Night around Zouma Valley. Multiple compact Shu assault groups move through different ravines toward separate Wei fieldworks, showing divided pressure rather than one mass charge. A few sections of wooden abatis burn with controlled low flames. Zhang He, a seasoned commander in his 50s with graying beard and dark Wei armor, reinforces the eastern enclosure while Xiahou Yuan, similar age, weathered and broad-shouldered in muted gray armor, redirects troops from the southern line. No people burning, no explosions, no giant inferno.', '小队数量、攻路、鹿角形制和两将位置为视觉重构;精卒分部夜攻张郃、夜烧围鹿角、渊分兵护东南围有史载。'), sources: ['《三国志·魏书·夏侯渊传》卷九', '《三国志·魏书·张郃传》卷十七'] },
{ id: '03', title: '主帅分兵救火', timePlace: '魏军南围,火后清晨', purpose: '呈现夏侯渊把半数所部助张郃并亲赴修补工事,形成暴露窗口。', narration: '张郃受攻,夏侯渊分出半数兵力增援,又亲赴南围修补被焚工事。', overlay: { heading: '主帅离阵', line: '每一个局部都需要救援时,指挥中枢反而最容易失去保护。' }, people: [{ name: '夏侯渊', office: '魏军主帅', action: '分兵助张郃,亲带轻兵赴南围救火修障' }, { name: '张郃', office: '魏军次帅', action: '在另一侧承受蜀军主攻' }], image: image('03-xiahou-yuan-divides-his-command', '清晨余烟中,夏侯渊站在受损鹿角旁指挥少量士卒修补工事,另一半援军沿山道赶往张郃东围,主帅与本阵之间出现空隙。', 'Smoke-gray morning after the night attacks. Xiahou Yuan stands beside damaged wooden abatis with only a light escort, directing soldiers to rebuild the southern barrier. A clearly separate reinforcement column moves away along a ridge toward Zhang He’s hard-pressed eastern enclosure, leaving a visible gap between the commander and his main camp. Fatigue, divided attention, practical repair tools and fieldworks. No attack yet, no corpse or foreshadowing through supernatural light.', '援军比例、山道、修障动作与空隙为视觉推演;夏侯渊分所将兵半助张郃并自赴救火、后遇袭战死有史载。'), sources: ['《三国志·魏书·夏侯渊传》卷九', '《三国志·魏书·张郃传》卷十七'] },
{ id: '04', title: '法正举旗·黄忠乘高', timePlace: '定军山高地,战机出现时', purpose: '把判断战机与居高突击连成一个战术节点。', narration: '法正从高处看准魏阵,举旗示意可击。黄忠乘高鼓噪,率军向下突阵。', overlay: { heading: '可击矣', line: '法正选中的是一瞬间;黄忠把这一瞬间变成了整座山的下冲。' }, people: [{ name: '法正', office: '刘备谋主', action: '从高处判断战机成熟并发出进击信号' }, { name: '黄忠', office: '讨虏将军', action: '率部乘高鼓噪、奋勇破敌' }, { name: '刘备', office: '蜀军主帅', action: '采纳法正判断,命黄忠出击' }], image: image('04-fa-zheng-signals-huang-zhong-descends', '定军山高地,法正举起无字令旗,年长黄忠率盾兵弩手伴随战鼓从坡顶整队下冲,刘备在后方指挥台观察。', 'On the high ground of Dingjun Mountain, Fa Zheng raises a plain blank signal flag after studying the divided Wei line below. Huang Zhong, a vigorous East Asian veteran in his late 60s with gray beard, weathered face and practical dark bronze Shu armor, leads shield infantry and crossbowmen downhill in tight order as field drums sound behind them. Liu Bei watches from a protected timber command platform. Strong downward geometry, no elderly caricature, no solo sword duel and no visible killing.', '令旗、鼓阵、兵种、坡度和人物外貌为视觉重构;法正判断可击、刘备命黄忠、黄忠乘高鼓噪攻之有史载。'), sources: ['《三国志·蜀书·法正传》卷三十七', '《三国志·蜀书·黄忠传》卷三十六', '《三国志·蜀书·先主传》卷三十二'] },
{ id: '05', title: '渊军失帅·张郃收军', timePlace: '定军山战后至阳平关方向', purpose: '交代夏侯渊战死与魏军并未被画成全军覆灭。', narration: '夏侯渊在交战中阵亡,魏军一度扰乱。郭淮收卒、推张郃主军,诸营退守阳平。', overlay: { heading: '失帅而未溃尽', line: '定军山决定汉中归属,却没有让魏军从这一刻完全消失。' }, people: [{ name: '夏侯渊', office: '魏征西将军', action: '救援南围时与蜀军接战而死;不展示遗体' }, { name: '郭淮', office: '征西将军司马', action: '收合散卒并推举张郃主持诸军' }, { name: '张郃', office: '魏将', action: '接掌军队、稳定各营并退守阳平' }, { name: '黄忠', office: '蜀将', action: '以战功迁征西将军' }], image: image('05-zhang-he-stabilizes-the-retreat', '战后烟尘中,夏侯渊空置的指挥席与倒伏帅旗暗示失帅;郭淮把将印交给张郃,魏军重新列队向阳平方向撤退,远处黄忠军占据高地。', 'Aftermath without bodies. An empty campaign chair and a fallen blank command standard imply Xiahou Yuan’s death off-screen. Guo Huai, a composed East Asian staff officer in his 40s, presents a command tally to the graying Zhang He as scattered Wei soldiers regroup into orderly columns withdrawing toward Yangping. High above in the distance Huang Zhong’s Shu formations secure the ridge rather than chase into massacre. Restrained gray-gold light, no corpse, severed head, triumphal pose or rout caricature.', '空席、将印交接、撤退秩序和双方距离为视觉重构;夏侯渊战死、郭淮收散卒、推张郃为军主、众心乃定并还阳平有史载。'), sources: ['《三国志·魏书·张郃传》卷十七', '《三国志·魏书·郭淮传》卷二十六', '《三国志·蜀书·黄忠传》卷三十六'] },
],
bgm: { catalogId: 'zhanzhen:dingjunshan#0', title: '定军山之战', version: '电影级配乐·黄忠版·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '第一幕留出山水纵深,第二幕用分散鼓点表现多点夜攻,第三幕抽空低频,第四幕乘高突击形成唯一高潮,第五幕降温至重新整军;删除歌词与品牌尾签。', reason: '项目音乐库有定军山专属黄忠版,结构与本包战术节点一致;仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:18', scene: '01', direction: '沔水与山势缓慢展开。' }, { from: '00:18', to: '00:36', scene: '02', direction: '多点夜攻以错位鼓点推进。' }, { from: '00:36', to: '00:53', scene: '03', direction: '抽空主鼓表现分兵后的空隙。' }, { from: '00:53', to: '01:11', scene: '04', direction: '法正举旗后全奏抬升。' }, { from: '01:11', to: '01:28', scene: '05', direction: '战后整军冷收。' }] },
sources: [{ claim: '南渡沔水、定军山势作营、黄忠乘高攻破魏军与汉中归属', citation: '《三国志·蜀书·先主传》卷三十二', url: 'https://zh.wikisource.org/zh-hans/三国志/卷32' }, { claim: '法正判断战机与黄忠奋勇破敌', citation: '《三国志·蜀书·法正传》《黄忠传》卷三十七、三十六', url: 'https://zh.wikisource.org/zh-hans/三国志/卷37' }, { claim: '夜烧围鹿角、夏侯渊分兵救火战死与张郃收军', citation: '《三国志·魏书·夏侯渊传》《张郃传》卷九、十七', url: 'https://zh.wikisource.org/zh-hans/三国志/卷09' }],
blacklist: ['加入黄忠与夏侯渊阵前单挑、定军山一刀斩将等小说镜头', '虚构雾、风、神迹为胜负决定因素', '把精卒万余误作刘备全军确数,或把魏军画成有明确总兵力', '展示夏侯渊遗体、斩首、血腥救火或尸堆', '现代文字、水印、后世服饰建筑、火器爆炸或动漫画风'],
},
{
dir: '诸葛亮南征(四郡皆平)',
event: { canonicalName: '诸葛亮南征(四郡皆平)', aliases: ['诸葛亮南征', '平定南中', '南中四郡平叛'], projectDynasty: '蜀汉', era: '蜀汉后主建兴三年', date: '公元225年三月至十二月', place: '越巂、益州、永昌、牂柯等南中地区', summary: '刘备去世后南中数郡叛乱,诸葛亮于225年亲率军南征,四郡皆平;战后改益州郡为建宁,并分置云南、兴古,以军事平定转入行政重组。' },
narration: { language: 'zh-CN', estimatedSeconds: 83, fullText: '刘备去世后,蜀汉政权尚未稳定,南中多地相继反叛。牂柯太守朱褒拥郡起兵;益州郡大姓雍闿据郡不服,把太守张裔流放到孙吴;越巂夷王高定也脱离蜀汉。建兴三年春,诸葛亮决定亲率军队南下。正史本纪对战斗过程写得极简,只留下“南征四郡,四郡皆平”八个字,因此画面不能把后世传说当成确定战况。能够确认的是,这不是一场单点决战,而是一次跨越山地与河谷、处理多个叛乱中心的连续行动。到当年秋天,平定完成。蜀汉随后把原益州郡改为建宁郡,又从建宁、永昌分置云南郡,从建宁、牂柯分置兴古郡。军事行动的结果,被固定成新的郡界、户籍和驻治体系。十二月,诸葛亮返回成都。小说与民间叙事中的七擒孟获、火烧藤甲兵,不属于陈寿本纪写明的节点;本包只呈现多路平叛、归附与行政接管,不给任何现代族群套上古代叛军身份。' },
scenes: [
{ id: '01', title: '乘丧并叛', timePlace: '公元223年至225年初,南中数郡', purpose: '用并行地点表现多个叛乱中心,而非单一蛮族大军。', narration: '先主去世后,朱褒、雍闿、高定分别在南中起兵或据郡不服。', overlay: { heading: '乘丧并叛', line: '南中的危机不是一面叛旗,而是数个地方权力同时脱离。' }, people: [{ name: '雍闿', office: '益州郡大姓', action: '据郡不宾,并把太守张裔流于吴' }, { name: '高定', office: '越巂夷王', action: '在越巂背叛蜀汉' }, { name: '朱褒', office: '牂柯太守', action: '以地方官身份拥郡而反' }], image: image('01-several-commandaries-rebel', '南中山地广角中,以河谷、山口和郡治三个分离空间表现不同地方武装各自封锁道路,服饰来源多样、不做统一异族符号。', 'Wide subtropical mountain landscape organized into three separated zones: a river-valley checkpoint controlled by local gentry retainers, a highland pass held by another mixed community force, and a modest commandery compound whose official troops have changed allegiance. Yong Kai, Gao Ding and Zhu Bao appear only as distinct distant leadership groups, not posed together. People wear varied late-Han frontier clothing with regional textiles, avoiding any direct copy of modern ethnic ceremonial dress. No uniform “tribal horde,” no exotic fantasy.', '三地同框、武装构成、道路封锁与服饰为视觉重构;朱褒拥郡反、雍闿据郡不宾、高定背叛有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三'] },
{ id: '02', title: '丞相亲征', timePlace: '建兴三年春,成都向南中山道', purpose: '呈现诸葛亮亲自带队进入险远地区。', narration: '三月,诸葛亮亲率军队南征,沿山道和河谷向多个郡推进。', overlay: { heading: '丞相亲征', line: '新丧之国要让远方重新服从,主政者选择亲自到场。' }, people: [{ name: '诸葛亮', office: '蜀汉丞相', action: '亲率军队南征四郡' }, { name: '蜀汉军民夫', office: '军队与转运人员', action: '沿山道分段运输粮械、架设便桥' }], image: image('02-zhuge-liang-leads-the-southern-march', '多雨山道上,诸葛亮穿朴素深灰袍与轻甲骑马随军,步卒民夫分段通过木桥和陡坡,地图木牍无字,不画羽扇神坛。', 'Spring rain in steep southern mountain country. Zhuge Liang, mid-40s, slim, composed, tied hair under a plain scholar’s cap, dark gray robe over light practical lamellar armor, rides modestly among staff rather than in a ceremonial chariot. Infantry, engineers and porters move in separated columns across a small timber bridge and along switchback paths, carrying grain and tools. Lush but realistic terrain, no feather-fan sorcery, altar, elephants, fantasy jungle or readable maps.', '路线、桥梁、天气、军队编组与人物外貌为视觉重构;建兴三年春三月诸葛亮亲率军南征有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·蜀书·诸葛亮传》卷三十五'] },
{ id: '03', title: '四郡皆平', timePlace: '建兴三年秋,南中各郡治与道路', purpose: '用解除关卡、接收印信表现平定,不虚构具体会战。', narration: '正史没有留下逐战细节,只确认到秋天四郡皆平,各处重新接受蜀汉政令。', overlay: { heading: '四郡皆平', line: '史书压缩成八个字的,是一整片山地重新接上政令。' }, people: [{ name: '诸葛亮', office: '南征主帅', action: '完成四郡平定并接受地方归附' }, { name: '南中地方首领与官吏', office: '各郡地方力量', action: '交还关卡、印信或重新接受任命;具体人名不臆断' }], image: image('03-four-commandaries-submit', '南中郡治外,几个来自不同河谷的地方代表分组交出无字木牍与关卡钥匙,蜀军解除路障但保持距离,诸葛亮主持接收。', 'Outside a modest late-Han frontier commandery, several clearly separate delegations from different valleys present blank wooden tallies, sealed cases and gate keys to Zhuge Liang’s officials. Shu soldiers dismantle road barriers and reopen a market path while keeping a respectful distance from civilians. No mass kneeling, no ethnic caricature and no invented central battle. Mixed local clothing remains varied, practical and historically generalized rather than copied from modern ceremonial costume.', '归附礼节、代表构成、郡治形制与解除路障为视觉重构;南征四郡、四郡皆平有史载,但逐战过程未详。'), sources: ['《三国志·蜀书·后主传》卷三十三'] },
{ id: '04', title: '改置三郡·十二月还成都', timePlace: '平定后至建兴三年十二月', purpose: '以郡界、户籍和归还成都交代长期结果。', narration: '蜀汉改益州郡为建宁,又分置云南、兴古。十二月,诸葛亮还成都。', overlay: { heading: '战后画界', line: '军队离开以后,真正长期留下的是三套新的官署与户籍。' }, people: [{ name: '诸葛亮', office: '蜀汉丞相', action: '主持平定后行政重组并于十二月还成都' }, { name: '新郡官吏与小吏', office: '建宁、云南、兴古郡行政人员', action: '重造边界图册、户籍与仓储清单' }], image: image('04-new-commandaries-and-return-to-chengdu', '新设郡治的木案上,官吏用绳结木片和无字简牍重分辖区,窗外百姓恢复集市;远景诸葛亮小队沿北向山路返回成都。', 'A newly organized frontier office after the campaign. Clerks use blank bamboo slips, sealed baskets, knotted cords and small terrain models to re-register communities for the new commanderies, with no readable map labels. Through the open courtyard a local market road has reopened. In a distant upper-background road, Zhuge Liang’s compact escort begins the northward return toward Chengdu under early-winter light. Administrative aftermath, not triumphal parade.', '官署工作、图册表现、市场恢复和返程同框为视觉重构;改置建宁、云南、兴古三郡及十二月还成都有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三'] },
],
bgm: { catalogId: 'tazhuan:zhugeliang#0', title: '诸葛亮', version: '电影级配乐·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '弱化人物颂歌感,前两幕以低弦与山路环境声推进,第三幕不做征服式高潮,第四幕用木质敲击转向行政收束;删除歌词、合唱和品牌尾签。', reason: '项目没有南征专属曲,采用项目自生成的诸葛亮器乐主题并重编为克制的行军—治理结构;仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:20', scene: '01', direction: '分散声部对应多地并叛。' }, { from: '00:20', to: '00:41', scene: '02', direction: '以步行节奏推进山道。' }, { from: '00:41', to: '01:02', scene: '03', direction: '保持克制,不做征服凯歌。' }, { from: '01:02', to: '01:23', scene: '04', direction: '木质脉冲与低弦安静收束。' }] },
sources: [{ claim: '朱褒、雍闿、高定叛乱及诸葛亮南征四郡皆平', citation: '《三国志·蜀书·后主传》卷三十三', url: 'https://zh.wikisource.org/zh-hans/三国志/卷33' }, { claim: '诸葛亮治蜀与南征的纪年框架', citation: '《三国志·蜀书·诸葛亮传》卷三十五', url: 'https://zh.wikisource.org/zh-hans/三国志/卷35' }],
blacklist: ['把七擒孟获、火烧藤甲兵、木鹿大王等后世叙事当作陈寿正文节点', '把南中各地塑造成同一民族、同一服饰的蛮族人海', '直接复制任何现代少数民族节庆服饰并标作古代叛军', '用屠村、焚寨、尸体表现四郡皆平,或把平叛做成征服奇观', '出现羽扇施法、祭坛妖术、巨象军团等幻想画面', '现代文字、水印、后世服饰建筑、火器爆炸或动漫画风'],
},
{
dir: '木门道之战(张郃追击中箭)',
event: { canonicalName: '木门道之战(张郃追击中箭)', aliases: ['木门道伏击战', '青封之战', '张郃木门中箭'], projectDynasty: '蜀汉', era: '蜀汉后主建兴九年、曹魏明帝太和五年', date: '公元231年二月至六月', place: '祁山、青封与木门道(记载地名并见)', summary: '诸葛亮第四次北伐因粮尽退军,魏将张郃奉命追击;蜀军在青封、木门道一带回身交战,张郃中箭身亡,北伐虽未获地却在退路上除去魏国宿将。' },
narration: { language: 'zh-CN', estimatedSeconds: 84, fullText: '建兴九年二月,诸葛亮再次出兵围攻祁山,并开始使用木牛运输军粮。蜀军深入陇右,补给仍是整场战役的上限。魏军由司马懿统率抵御,双方在祁山与上邽一带相持。到了六月,蜀军粮食耗尽,诸葛亮下令退军。退兵最怕追军咬住队尾,因此蜀军不是仓皇逃走,而是在山道中保持队列,选择可以回身作战的位置。魏国宿将张郃率兵追来。蜀汉后主传记作“追至青封”,张郃本传则记作“追至木门”,两种地名都应保留,不能强行合成一个精确坐标。追军进入狭窄地形后,蜀军从坡地和林间发射弩箭。正史只确认张郃与诸葛亮军交战,飞箭射中右膝,随后去世;没有写明是哪位神射手,也没有个人决斗。张郃死后谥为壮侯。诸葛亮此役因粮尽未能继续攻取,但撤军没有变成溃败,反而让魏国失去一员从汉末征战数十年的名将。' },
scenes: [
{ id: '01', title: '复围祁山·木牛运粮', timePlace: '公元231年二月,祁山山道', purpose: '建立第四次北伐与新运输工具仍无法根治粮道问题。', narration: '诸葛亮复出围祁山,开始用木牛运运粮。山道上的每一车粮都决定前线能停多久。', overlay: { heading: '木牛运粮', line: '新工具能省力,却不能缩短从汉中到陇右的山路。' }, people: [{ name: '诸葛亮', office: '蜀汉丞相', action: '复出围祁山并组织木牛转运' }, { name: '转运军民', office: '蜀军后勤人员', action: '沿狭窄山道推动木制独轮或多轮运输器' }], image: image('01-wooden-oxen-supply-qishan', '祁山栈道与土路上,军民推动结构朴素的木牛式运输车运粮,诸葛亮在路旁核对无字木牍,远处围营不夸张。', 'Mountain supply road near Qishan. Teams of Shu soldiers and porters push plausible simple wooden “wooden ox” handcarts—compact load-bearing carts with no animal sculpture or fantasy mechanism—carrying grain sacks over switchbacks and rough plank sections. Zhuge Liang, early 50s, slim, composed, plain cap and dark robe over light armor, checks blank tallies beside the road. Distant siege camps remain small. Emphasize logistics, slopes and fatigue, no mechanical steampunk device or readable text.', '木牛具体结构、道路、载重和人物动作是审慎视觉重构;始以木牛运、复出围祁山有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三'] },
{ id: '02', title: '粮尽退军', timePlace: '六月,祁山至青封山道', purpose: '表现有组织的撤退,而非战败溃逃。', narration: '六月,军粮耗尽。诸葛亮分层撤军,把辎重和弩手安排在可互相掩护的山道。', overlay: { heading: '粮尽而退', line: '这场北伐先被粮道叫停,真正的危险才从退路开始。' }, people: [{ name: '诸葛亮', office: '蜀军主帅', action: '因粮尽下令退军并组织后卫' }, { name: '蜀军弩手与转运队', office: '后卫和辎重', action: '分层通过狭道,准备阻滞追兵' }], image: image('02-ordered-retreat-through-mountain-road', '陇右山道中,粮袋见底的车队先行,蜀军盾弩手在不同坡台交替掩护,诸葛亮指挥队伍有序后撤。', 'Dry early-summer mountain corridor in Longyou. Nearly empty grain carts and tired porters move first through the pass while Shu shield and crossbow units occupy alternating terraces to cover one another. Zhuge Liang directs spacing from a modest roadside command point. The column is orderly and tense, not fleeing; discarded supplies are minimal. Wide depth makes the exposed rear guard visible. No battle yet, no readable signs or fantasy fortifications.', '车队次序、坡台掩护、粮袋数量与指挥位置为视觉重构;六月粮尽退军有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·蜀书·诸葛亮传》卷三十五'] },
{ id: '03', title: '宿将衔尾追来', timePlace: '青封、木门道方向', purpose: '建立张郃追击与山道逐渐收窄的风险。', narration: '张郃率魏军追击。追军沿着蜀军留下的道路进入青封、木门一带。', overlay: { heading: '归军勿追', line: '追击能把撤军变成溃败,也可能让追军自己走进准备好的地形。' }, people: [{ name: '张郃', office: '魏车骑将军', action: '率军追击诸葛亮退军' }, { name: '魏军前锋', office: '追击部队', action: '沿山道衔尾推进并侦察高处' }], image: image('03-zhang-he-pursues-into-the-pass', '魏国宿将张郃率前锋沿干燥山谷追击,宽路逐渐变成两壁夹峙的木门道,斥候仰看安静坡林,前方只见蜀军远去尘迹。', 'Veteran Zhang He, now in his late 60s with graying beard, lean face and practical dark gray Wei lamellar armor, leads a measured pursuit column from an open dry valley into a narrowing wooded gorge. Scouts study the quiet slopes while only distant Shu dust marks remain ahead. The landscape gradually closes like a gate, but avoid obvious trap arrows or theatrical foreknowledge. Grounded horses, infantry and late Three Kingdoms gear, no cavalry stampede.', '人物年龄、道路收窄、侦察方式和部队规模为视觉重构;张郃追至青封或木门、与亮军交战有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·魏书·张郃传》卷十七'] },
{ id: '04', title: '坡地弩阵回击', timePlace: '木门道狭窄段', purpose: '表现撤军后卫利用高差回身交战,不指定射箭者。', narration: '魏军进入狭道,蜀军在坡地回身列弩。第一轮箭雨压向追击前锋。', overlay: { heading: '退路回身', line: '退军保住队形以后,山道就能从退路变成射界。' }, people: [{ name: '蜀军后卫', office: '弩手与盾兵', action: '在坡地与林缘回身阻击追军' }, { name: '张郃', office: '魏军追将', action: '率前锋进入弩箭覆盖的狭道' }], image: image('04-crossbows-fire-from-the-slopes', '狭窄山道两侧坡台上,蜀军弩手在盾牌后分层齐射,魏军前锋举盾停步,张郃在队中抬手整队,不画箭矢穿体。', 'Narrow mountain corridor at Mumen. Shu crossbow teams on staggered side terraces fire disciplined volleys from behind shields and timber screens. Below, the Wei vanguard raises shields and halts, trying to maintain formation; Zhang He remains visibly within the command group, signaling order rather than charging alone. Arrows strike earth, shields and carts only—none visibly penetrate bodies. Clear vertical tactical geometry, dust and filtered forest light, no gore.', '弩手位置、齐射节奏、盾阵和张郃所处位置为视觉推演;张郃追至木门与亮军交战、飞矢中膝有史载,但射手身份不详。'), sources: ['《三国志·魏书·张郃传》卷十七'] },
{ id: '05', title: '飞矢中郃·追击停止', timePlace: '木门道或青封,交战末段', purpose: '以张郃受伤、魏军护送撤出和蜀军继续退军收束。', narration: '交战中,飞箭射中张郃右膝。他被部下带离,随后去世;魏军追击就此停止。', overlay: { heading: '追击止于木门', line: '北伐没能前进一步,却在退路上带走了魏国的一员宿将。' }, people: [{ name: '张郃', office: '魏车骑将军', action: '交战中右膝中箭,随后去世;不展示伤口' }, { name: '魏军亲兵', office: '追击队护卫', action: '用盾牌遮护并把受伤主将带离' }, { name: '蜀军后卫', office: '撤军掩护部队', action: '停止追射,继续按序撤离' }], image: image('05-pursuit-ends-at-mumen', '山道箭雨渐停,魏军亲兵用盾围住坐上简易担架的张郃,甲裙遮住伤处;远处蜀军弩手撤离坡地,主力车队继续远去。', 'After the volley, Wei guards form a shield canopy around Zhang He as they place him on a simple field litter; his armored skirt and a clean wrapped knee conceal the injury completely, with no blood. The pursuit column turns back out of the gorge in controlled concern. In the distance Shu crossbow teams withdraw from the terraces and their supply column continues away. Somber restraint, no corpse, arrow embedded in flesh, triumphant archer or named killer.', '包扎、担架、撤出顺序与双方克制距离为视觉重构;张郃右膝中飞矢、薨逝与追击停止有史载。'), sources: ['《三国志·魏书·张郃传》卷十七', '《三国志·蜀书·后主传》卷三十三'] },
],
bgm: { catalogId: 'zhanzhen:jieting#0', title: '街亭之战', version: '张郃主题器乐 stem·反向使用', status: 'pending-export', pathOrUrl: '', usage: '将项目内张郃主题从街亭胜势反向改编为追击中的不安:前三幕保持克制,第四幕弩阵短促爆发,第五幕抽去鼓点;删除歌词和品牌尾签。', reason: '项目没有木门道专属曲;街亭电影级配乐的张郃主题可形成三年前胜、三年后亡的回响,仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:17', scene: '01', direction: '机械木质脉冲表现运输。' }, { from: '00:17', to: '00:34', scene: '02', direction: '降低重心进入退军。' }, { from: '00:34', to: '00:51', scene: '03', direction: '追击主题逐渐收窄。' }, { from: '00:51', to: '01:08', scene: '04', direction: '弩阵形成一次短促爆发。' }, { from: '01:08', to: '01:24', scene: '05', direction: '鼓点消失,只留低弦。' }] },
sources: [{ claim: '围祁山、木牛运粮、粮尽退军、张郃追至青封被箭死', citation: '《三国志·蜀书·后主传》卷三十三', url: 'https://zh.wikisource.org/zh-hans/三国志/卷33' }, { claim: '张郃追至木门、与亮军交战、右膝中飞矢而薨', citation: '《三国志·魏书·张郃传》卷十七', url: 'https://zh.wikisource.org/zh-hans/三国志/卷17' }],
blacklist: ['强行断言青封与木门是完全相同的精确坐标;两种地名应并存', '指定诸葛亮、姜维或某位虚构神射手亲自射中张郃', '把木牛画成自动机械兽、机关战车或蒸汽装置', '表现箭穿膝、流血、尸体,或以敌方死亡作胜利特写', '现代文字、水印、后世服饰建筑、火器爆炸或动漫画风'],
},
{
dir: '洮西之战(姜维大破王经)',
event: { canonicalName: '洮西之战(姜维大破王经)', aliases: ['洮西之战', '故关之战', '姜维破王经'], projectDynasty: '蜀汉', era: '蜀汉后主延熙十八年、曹魏高贵乡公正元二年', date: '公元255年夏秋', place: '洮水西岸、故关、狄道与钟题', summary: '姜维率军出狄道,在洮西大败魏雍州刺史王经;王经退保狄道。陈泰率援军抵达后解围,姜维未能攻城,退屯钟题。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '延熙十八年,姜维再次率军进入陇右,向狄道推进。魏国雍州刺史王经仓促迎战。魏方记载,王经在故关一带与蜀军交锋,退到洮水以西后遭到大败;蜀汉本纪则简洁地写作“与王经战于洮西,大破之”。姜维抓住魏军阵线不稳的时机,分队越过河谷,从正面和侧面压迫。王经部众失去秩序,只能退入狄道城。洮西成为蜀汉后期少见的大捷,但胜利并没有自动带来城池。姜维随即围攻狄道,城中粮食和士气都十分紧张。魏国征西将军陈泰率援军进入陇右,绕行到狄道东南高城岭一带,夜里多举烽火、鸣鼓角,让城内知道援军已经到达。王经军心恢复,姜维则面临外援与坚城的双重压力。最终他没有在狄道城下耗尽兵力,解除包围,退屯钟题。洮西说明蜀军仍能打出一场漂亮的野战,却也说明一次“大破”不足以改变国力与后勤的长期差距。八年以后,蜀汉灭亡。' },
scenes: [
{ id: '01', title: '复出狄道', timePlace: '公元255年,陇右狄道方向', purpose: '建立姜维再度北进与王经仓促迎战。', narration: '姜维率诸军出狄道。王经没有退守坚城,而是在故关、洮水一线迎战。', overlay: { heading: '复出狄道', line: '两军都在抢时间:一方要在援军前取胜,一方想在城外截住进军。' }, people: [{ name: '姜维', office: '蜀汉卫将军', action: '率诸军进入陇右、趋向狄道' }, { name: '王经', office: '曹魏雍州刺史', action: '组织州郡兵在城外迎战' }], image: image('01-jiang-wei-advances-on-didao', '洮河上游宽阔谷地,姜维率蜀军沿河岸分路推进,王经在远处故关附近仓促集结州郡兵,狄道土城轮廓位于更后方。', 'Broad upper Tao River valley. Jiang Wei, early 50s, lean, focused, gray-streaked moustache and short beard, practical dark teal-brown Shu lamellar armor, leads several separated columns along the riverbank toward Didao. In the distance Wang Jing, a middle-aged Wei provincial commander in muted gray armor, hurriedly assembles mixed garrison and field troops near a modest pass while the low rammed-earth outline of Didao lies farther behind. Clear strategic depth, no giant fortress or exact-number horde.', '行军路线、故关与狄道相对位置、人物外貌及部队构成为视觉重构;姜维出狄道、王经出城迎战有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·魏书·陈泰传》卷二十二'] },
{ id: '02', title: '洮水西岸大破', timePlace: '故关退路至洮西', purpose: '表现魏军渡河后阵线失序与蜀军多向压迫,不用夸大伤亡。', narration: '王经退到洮水西岸,蜀军趁其阵列不稳进击。魏军大败,向狄道退却。', overlay: { heading: '洮西大破', line: '河流压住退路以后,一次阵形松动就会沿整条河岸传开。' }, people: [{ name: '姜维', office: '蜀军主帅', action: '抓住魏军临水阵线不稳发动进击' }, { name: '王经', office: '魏雍州刺史', action: '野战失利后组织部众退保狄道' }], image: image('02-victory-west-of-the-tao-river', '洮水西岸砾石滩与台地间,蜀军盾弩步卒从正面推进,小队沿高岸侧压;魏军放弃临水阵位有序向狄道方向撤退,不画溺亡尸体。', 'On gravel bars and terraces west of the Tao River, Shu shield and crossbow infantry press the unstable Wei line frontally while smaller units descend from a higher bank onto one flank. Wang Jing signals a retreat toward Didao as Wei formations disengage under pressure, some supply carts bogged near the water. Show operational defeat without massacre: no drowning people, corpses, bloody weapons or impossible numbers. Cool river light and dusty movement.', '交战队形、侧压路线、车辆受阻和撤退秩序为视觉推演;与王经战于洮西大破之、经退保狄道有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·魏书·陈泰传》卷二十二'] },
{ id: '03', title: '王经退保狄道', timePlace: '狄道城内外', purpose: '把野战胜利转入围城困局。', narration: '王经退入狄道,姜维包围城池。城外是胜军,城内仍有可守的墙与等待的援兵。', overlay: { heading: '退保狄道', line: '野战输掉以后,城墙把失败暂时停在了门外。' }, people: [{ name: '王经', office: '狄道守军主将', action: '收余军入城坚守' }, { name: '姜维', office: '蜀军主帅', action: '乘胜围狄道,但未能立即拔城' }, { name: '狄道军民', office: '城中守军与居民', action: '修补城防、分配有限水粮' }], image: image('03-wang-jing-holds-didao', '低矮夯土狄道城门关闭,王经在城头重整残军,城内军民运水修墙;城外姜维军建立分散围营但没有巨型攻城器。', 'Low rammed-earth walls of Didao after the field defeat. Wang Jing reorganizes tired defenders on the parapet while soldiers and civilians carry water, repair timber braces and count a few grain baskets. Outside, Jiang Wei’s troops establish separated camps and observation posts rather than an unbroken fantasy ring. Both sides are exhausted. No giant trebuchets, palace towers, famine corpses or assault spectacle.', '城墙形制、围营密度、军民分工和物资状态为视觉重构;王经退保狄道、姜维进围有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·魏书·陈泰传》卷二十二'] },
{ id: '04', title: '高城岭烽火解围', timePlace: '狄道东南高城岭,夜间', purpose: '呈现陈泰援军以烽火鼓角与地形制造到援信号。', narration: '陈泰援军绕到高城岭,夜举烽火、鸣鼓角。狄道守军知道援军已至,士气重新稳定。', overlay: { heading: '岭上火起', line: '几堆烽火先越过包围,把援军到达的消息送进城里。' }, people: [{ name: '陈泰', office: '曹魏征西将军', action: '率援军进入陇右并在高城岭显示兵势' }, { name: '王经', office: '狄道守将', action: '从城上确认援军到达' }, { name: '姜维', office: '蜀军主帅', action: '面对城内外呼应重新评估围城' }], image: image('04-beacons-on-gaocheng-ridge', '夜色高城岭上,陈泰援军依次点起数处烽火并鸣鼓角,远方狄道城头以灯火回应;山谷下蜀军围营转向观察来援方向。', 'Night on Gaocheng Ridge southeast of Didao. Chen Tai, a composed East Asian commander in his late 40s with plain dark Wei armor and scholar-general bearing, orders several separated beacon fires lit along the ridge while drums and horns are implied by musicians in silhouette. Far across the valley small lights answer from Didao’s wall. Below, Shu siege camps turn shields and scouts toward the new threat. Signal and morale, not magical fire or a city-wide blaze.', '烽火数量、山岭视线、城上回应和围营反应为视觉重构;陈泰援军抵达、夜举烽火鼓角以通城内有史载。'), sources: ['《三国志·魏书·陈泰传》卷二十二'] },
{ id: '05', title: '解围退屯钟题', timePlace: '狄道至钟题方向', purpose: '明确蜀军未拔狄道,胜利以主动解围后撤结束。', narration: '援军已到,狄道仍未攻下。姜维解除包围,率军退屯钟题。', overlay: { heading: '大胜未能拔城', line: '洮西赢下了野战,却没有把胜利变成一座城。' }, people: [{ name: '姜维', office: '蜀汉卫将军', action: '权衡援军与坚城后解围退屯钟题' }, { name: '陈泰、王经', office: '魏援军与守军将领', action: '内外呼应,使狄道解围' }], image: image('05-jiang-wei-withdraws-to-zhongti', '清晨狄道城外,蜀军有序拆除围营、装车向钟题方向撤走,姜维在后队回望;陈泰援军与王经守军保持距离会合,不画追杀。', 'Cold morning outside Didao. Shu soldiers methodically dismantle fieldworks, load tools and grain onto carts, and withdraw in ordered columns toward Zhongti. Jiang Wei rides with the rear guard and looks back at the unbroken earth-walled city. At a respectful distance Chen Tai’s relief force links with Wang Jing’s defenders without launching a massacre pursuit. The image must feel like a limited victory reaching its logistical boundary, not a rout.', '拆营过程、会师距离、返程方向与人物位置为视觉重构;姜维未拔狄道、解围退屯钟题有史载。'), sources: ['《三国志·蜀书·后主传》卷三十三', '《三国志·魏书·陈泰传》卷二十二'] },
],
bgm: { catalogId: 'mingchangmian:wuzhangyuan_xingluo#2', title: '五丈原·秋风星落', version: '姜维承志版·仅复用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '移除五丈原人声叙事,把姜维主题改为先扬后收:第二幕形成野战高潮,第三四幕转为围城压力,第五幕不胜利凯旋而以未拔城的低音结束;删除品牌尾签。', reason: '项目没有洮西专属曲;音乐库中的姜维承志主题与蜀汉后期视角相符,仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:17', scene: '01', direction: '行军主题缓慢推进。' }, { from: '00:17', to: '00:35', scene: '02', direction: '洮西野战形成主要高潮。' }, { from: '00:35', to: '00:52', scene: '03', direction: '转入持续围城低频。' }, { from: '00:52', to: '01:09', scene: '04', direction: '烽火出现时改变和声重心。' }, { from: '01:09', to: '01:26', scene: '05', direction: '主动撤围后不完全解决地收束。' }] },
sources: [{ claim: '姜维出狄道、战于洮西大破王经、王经退保狄道与姜维退屯钟题', citation: '《三国志·蜀书·后主传》卷三十三', url: 'https://zh.wikisource.org/zh-hans/三国志/卷33' }, { claim: '王经野战失利、狄道被围与陈泰援军解围过程', citation: '《三国志·魏书·陈泰传》卷二十二', url: 'https://zh.wikisource.org/zh-hans/三国志/卷22' }, { claim: '姜维后期军事行动及其局限', citation: '《三国志·蜀书·蒋琬费祎姜维传》卷四十四', url: 'https://zh.wikisource.org/zh-hans/三国志/卷44' }],
blacklist: ['把洮西大胜写成攻克狄道;王经退保城内,姜维最终退屯钟题', '把魏军伤亡数字做成精确可视人海或尸体场面', '把烽火鼓角画成神火、信号弹或火药爆炸', '将姜维表现成单骑击溃魏军,忽略河岸地形与部队协同', '现代文字、水印、后世服饰建筑、火器爆炸或动漫画风'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((person) => `${person.name}(${person.office};${person.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((cue) => `| ${cue.from}–${cue.to} | ${cue.scene} | ${cue.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
}
console.log(`Generated ${packs.length} Shu Han core battle packages`)
@@ -0,0 +1,86 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed fifth-to-sixth-century China, cinematic 16:9 landscape, subtle film grain, physically plausible armies and structures, rammed earth, aged timber, hemp, low-sheen silk, leather, iron lamellar armor, straight swords, bows, crossbows, spears, cavalry tack, wooden carts and period river craft. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Liu Song, Southern Qi and Northern Wei story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const cue = (scene, from, to, direction) => ({ scene, from, to, direction })
const packs = [
{
dir: '刘宋/元嘉北伐(滑台溃败至瓜步)',
event: { canonicalName: '元嘉北伐(滑台溃败至瓜步)', aliases: ['元嘉北伐', '王玄谟北伐', '北魏至瓜步'], projectDynasty: '刘宋', era: '刘宋文帝元嘉二十七年至二十八年、北魏太武帝太平真君十一年至十二年', date: '公元450年七月至451年正月', place: '黄河滑台、淮南尉武戍、盱眙与长江瓜步', summary: '刘宋文帝遣王玄谟等北伐,前锋顿兵滑台而败;北魏太武帝随即大举南下,刘康祖等沿途战死,魏军直抵瓜步后北撤,淮南六州遭到严重破坏。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '元嘉二十七年,宋文帝刘义隆再次发动北伐,命王玄谟等分路北进。宋军前锋抵达黄河边的滑台,却没能迅速攻下城池;围攻拖延,军需和民心同时受损。北魏援军逼近时,王玄谟部突然溃退,北伐由进攻转成了全面防守。魏太武帝拓跋焘乘势南下,骑兵越过黄河、淮河,一路逼向长江。刘宋将领刘康祖在尉武戍迎战,兵败被杀;盱眙等城则闭门坚守,使魏军无法稳固占领淮南。十二月,拓跋焘抵达瓜步,隔江与建康相望。“饮马长江”是这次兵锋抵江的象征,不等于北魏已经渡江攻入首都。次年正月,魏军从瓜步北撤,沿线人口、村落和物资却已遭到巨大损失。元嘉北伐没有收复河南,反而让刘宋的富庶腹地承担反攻。这场失败也成为辛弃疾后来“元嘉草草,封狼居胥,赢得仓皇北顾”所指的历史教训。' },
scenes: [
{ id: '01', title: '锐意恢复·诸军北进', timePlace: '建康军府与淮泗水路', purpose: '建立文帝决策与多路北伐的战略开端。', narration: '宋文帝命王玄谟等分路北伐,水陆军沿淮泗向黄河推进。', overlay: { heading: '再取河南', line: '恢复故土的命令从建康发出,真正的压力却沿每一条粮道向北累积。' }, people: [{ name: '刘义隆', office: '刘宋文帝', action: '在建康下令北伐并持续干预前线' }, { name: '王玄谟', office: '宁朔将军', action: '率前锋北上围攻滑台' }, { name: '沈庆之', office: '武将、随军参议', action: '对深入北伐持保留意见' }], image: image('01-emperor-orders-northern-expedition', '建康简朴军府内宋文帝面对无字木牍布置北伐,门外淮河浅底船装载弩、粮袋与马料,王玄谟在码头受令,画面以廊柱连接决策和后勤。', 'At a restrained Jiankang military court, Emperor Liu Yilong in his early 40s studies blank wooden tallies with officers. Through an open timber colonnade, shallow Huai River transports load crossbows, grain sacks and fodder as Wang Xuanmo prepares to depart. Join court decision and logistics through architecture, not a magical split screen or literal map.', '军府、装船与人物同框为视觉重构;文帝遣王玄谟北伐及多路进军有史载。'), sources: ['《宋书·文帝纪》卷五', '《资治通鉴》卷一百二十五'] },
{ id: '02', title: '顿兵滑台·魏援逼近', timePlace: '黄河南岸滑台城外', purpose: '解释前锋为何由围攻转成溃退。', narration: '王玄谟围滑台久攻不下。魏援逼近,宋军来不及从容撤围,前锋很快崩溃。', overlay: { heading: '一城顿师', line: '攻不下滑台,北伐所有后路都要替这座城承担时间。' }, people: [{ name: '王玄谟', office: '北伐前锋主帅', action: '围攻滑台,闻魏援至后退军' }, { name: '北魏滑台守军', office: '守城军', action: '凭城拖住宋军' }, { name: '刘宋军士与役夫', office: '攻城与转运部队', action: '拆营撤退,队列开始失序' }], image: image('02-huatai-siege-breaks', '黄河平原上的滑台夯土城久围未下,宋军正在拆除木栅和装车后撤;远方尘线上出现北魏骑兵援军,王玄谟试图收束前锋,尚未画成屠杀。', 'At compact rammed-earth Huatai on the Yellow River plain, a weathered Liu Song siege camp is being dismantled after a failed assault. Soldiers load crossbows and grain onto carts while Wang Xuanmo tries to form a withdrawal column. A distant cavalry dust line announces Northern Wei relief forces. Show the moment control begins to fail, not a giant fortress, catastrophic melee or bodies.', '滑台城形制、撤营次序与魏援尘线为视觉推演;围城不克、魏援至而宋军溃退有史载。'), sources: ['《宋书·王玄谟传》卷七十六', '《资治通鉴》卷一百二十五'] },
{ id: '03', title: '魏骑南下·尉武拒战', timePlace: '寿阳尉武戍与淮南道路', purpose: '以刘康祖战败表现北魏反攻速度。', narration: '北魏骑兵乘胜越淮南下。刘康祖在尉武戍迎战,力不能支,战败身死。', overlay: { heading: '北伐变南侵', line: '几个月前向北运输的道路,如今成了魏骑向南推进的通道。' }, people: [{ name: '刘康祖', office: '左军将军', action: '在尉武戍列阵拒敌,战败被杀;不展示遗体' }, { name: '拓跋焘', office: '北魏太武帝', action: '统军快速南下' }, { name: '淮南百姓', office: '编户与避兵者', action: '携家离开道路与聚落' }], image: image('03-liu-kangzu-at-weiwu-garrison', '淮南冬日尉武小戍前,刘康祖率宋军盾弩列阵阻击正在展开的北魏骑兵;侧后方百姓推车避入芦苇水网,不画刘康祖死亡或尸体。', 'At a modest Weiwu stockade in winter Huainan, Liu Kangzu forms Liu Song shield and crossbow infantry across a wet road while Northern Wei cavalry begins to deploy beyond bow range. Behind the defense, civilian families push handcarts into reed-lined waterways. Frame a doomed delaying action and the human cost without showing death, gore, burning civilians or impossible cavalry masses.', '戍堡、阵列、百姓撤离和交战瞬间为视觉重构;刘康祖在尉武戍与魏军战败被杀有史载。'), sources: ['《宋书·文帝纪》卷五', '《魏书·世祖纪》卷四下'] },
{ id: '04', title: '兵临瓜步·北撤留墟', timePlace: '长江北岸瓜步与建康对岸', purpose: '交代魏军抵江但未渡江,以及撤退后的淮南损失。', narration: '拓跋焘抵达瓜步,隔江望见建康。次年魏军北撤,留下受损的六州与空村。', overlay: { heading: '隔江而止', line: '魏军看见了建康,却没有越过长江;真正留下的是漫长的返乡路。' }, people: [{ name: '拓跋焘', office: '北魏太武帝', action: '在瓜步临江驻军,后率军北撤' }, { name: '刘宋江防军', office: '建康沿江守军', action: '隔江警戒,不虚构渡江决战' }, { name: '淮南流民', office: '战后返乡者', action: '经过烧毁田舍与空粮仓' }], image: image('04-guabu-standoff-and-ruined-huainan', '冬日瓜步长江北岸,北魏骑兵与马匹在江边汲水,拓跋焘望向雾中建康城影;近景以返乡百姓和空粮车通向受损村落,江上没有大规模渡船。', 'Winter at Guabu on the north bank of the Yangtze. Tuoba Tao watches the faint silhouette of Jiankang across the broad river while cavalry horses drink at a quiet inlet, a restrained visual echo of “watering horses at the Yangtze.” In the foreground returning Huainan families pass empty carts and damaged farmsteads. No mass crossing, city assault, celebration or literal poem text.', '饮马、建康可见度、返乡百姓与受损村落为视觉转译;魏军至瓜步后北撤、淮南残破有史载。'), sources: ['《宋书·文帝纪》卷五', '《资治通鉴》卷一百二十五'] },
],
bgm: { catalogId: 'zhanzhen:tumu#0', title: '土木堡之变', version: '电影级配乐·灾难性溃退结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '前段抽掉明代音色,以低弦和粮车节奏推进;滑台撤围时打击错位,尉武拒战形成短峰,瓜步只留寒风与低音;删除人声、歌词和品牌尾签。', reason: '项目暂无元嘉北伐专曲,借用同为主动出兵转成全线溃退的项目自生成配乐结构,只取无时代指向的器乐层;仓库暂无成品音频。', cues: [cue('01','00:00','00:21','粮道节拍克制推进。'),cue('02','00:21','00:43','拆营时拍点逐渐失稳。'),cue('03','00:43','01:04','尉武拒战形成唯一短峰。'),cue('04','01:04','01:26','临江不爆发,冷风收尾。')] },
sources: [{ claim: '王玄谟北伐、刘康祖战死与魏军自瓜步退走', citation: '《宋书·文帝纪》卷五', url: 'https://zh.wikisource.org/zh-hans/宋书/卷005' }, { claim: '滑台围攻、北魏南下与瓜步对峙日序', citation: '《资治通鉴》卷一百二十五', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷125' }, { claim: '王玄谟围滑台的前线经过', citation: '《宋书·王玄谟传》卷七十六', url: 'https://zh.wikisource.org/zh-hans/宋书/卷076' }],
blacklist: ['把“饮马长江”画成北魏已渡江攻入建康', '只归因于单一将领怯懦,忽略围城、补给与皇帝遥控', '用辛弃疾词句反向虚构当时军中对白或旗号', '把北魏骑兵画成蒙古帝国或清代八旗', '现代文字、水印、后世服甲建筑、火器、尸堆与血腥'],
},
{
dir: '南齐/崔慧景之乱(十二日围台城)',
event: { canonicalName: '崔慧景之乱(十二日围台城)', aliases: ['崔慧景反', '崔慧景袭京师', '南齐永元之乱'], projectDynasty: '南齐', era: '南齐东昏侯永元二年', date: '公元500年三月至四月', place: '广陵、京口、蒋山与建康台城', summary: '奉命讨伐寿阳的护军将军崔慧景在广陵回军反叛,渡江攻破沿途堡垒,绕蒋山逼入建康并围台城十二日;萧懿援军渡江后其前锋败退,部将倒戈,崔慧景逃亡途中被杀。' },
narration: { language: 'zh-CN', estimatedSeconds: 87, fullText: '永元二年,南齐朝廷命护军将军崔慧景率军北上,讨伐据寿阳降魏的裴叔业。东昏侯萧宝卷亲自在琅邪城送行,却只准崔慧景单骑入围。离开京城后,崔慧景在广陵召集军将,宣称皇帝昏乱,随即调转军队南下。叛军渡江到京口,攻破竹里诸垒;行军时,他们用少量船只沿江运酒肉,发现官军堡内升起炊烟就趁守军进食猛攻。抵达蒋山后,崔慧景听从山民建议,沿龙尾山道鱼贯夜进,从意料之外的位置逼近建康。朝廷军队接连溃散,崔慧景进入乐游苑,围住台城,却在城门已经紧闭后犹豫不决,也没有建立稳固营垒。十二日后,萧懿援军从采石渡江。崔慧景仍判断台城即将投降,没有提前阻渡;其子崔觉出战失败,崔恭祖等部将转而向朝廷投降。军心迅速瓦解,崔慧景只带数名亲信逃走,最后在蟹浦被渔父杀死。这场叛乱一度逼到宫门,却败在错失时机、部众离心和救兵渡江。' },
scenes: [
{ id: '01', title: '奉诏出城·广陵回军', timePlace: '琅邪城至广陵', purpose: '呈现一次朝廷征讨如何反转成逼宫。', narration: '崔慧景奉命出征寿阳,离开建康后在广陵召集军将,调转兵锋。', overlay: { heading: '出城即回师', line: '皇帝把最不放心的将军送出了城,也把一支现成军队交到了他手里。' }, people: [{ name: '崔慧景', office: '护军将军、平西将军', action: '奉命北上,至广陵回军反叛' }, { name: '萧宝卷', office: '南齐皇帝、后追废为东昏侯', action: '在琅邪城严密送行并命其讨寿阳' }, { name: '崔觉', office: '直阁将军、崔慧景之子', action: '按约出奔,与父军会合' }], image: image('01-cui-huijing-turns-at-guangling', '春日广陵军营,崔慧景站在无字军令木案前向诸军主宣布回师,列队士卒把原本向北的粮车和旗帜转向南方江岸;远景是建康方向薄雾。', 'In a spring camp outside Guangling, the 63-year-old veteran Cui Huijing, lean and gray-bearded in restrained Southern Qi lamellar armor, addresses assembled unit commanders over a blank dispatch table. Soldiers physically turn supply carts and blank standards from the northern road toward the Yangtze and Jiankang. Show a deliberate change of direction, not cheering crowds, a coup proclamation with text or a heroic coronation.', '军营位置、转动车队和军将会议为视觉重构;崔慧景至广陵召诸军主回军反叛有史载。'), sources: ['《南齐书·崔慧景传》卷五十一', '《南齐书·东昏侯纪》卷七'] },
{ id: '02', title: '渡江破垒·以炊烟觇敌', timePlace: '京口至竹里诸垒', purpose: '展示叛军快速攻破沿江防线的具体战术。', narration: '叛军由京口渡江,以船载粮;见守垒炊烟便趁机猛攻,竹里防线失守。', overlay: { heading: '看烟进攻', line: '堡里一生火做饭,城外就知道守军最松的一刻到了。' }, people: [{ name: '崔恭祖、崔觉', office: '叛军前锋', action: '率轻装部队攻竹里诸垒' }, { name: '张佛护', office: '骁骑将军、朝廷守将', action: '拒守竹里,城陷后被杀;不展示遗体' }, { name: '崔慧景', office: '叛军主帅', action: '沿江组织舟运与连续进攻' }], image: image('02-attack-when-cooking-smoke-rises', '京口竹里小型夯土堡,守军营灶刚升起几缕炊烟,崔恭祖与崔觉率轻装盾矛兵从江岸芦苇间迅速接近;沿江小船载着酒肉袋,不画城陷后的斩首。', 'At small rammed-earth Zhuli field forts near Jingkou, thin cooking smoke rises from government garrison hearths. Cui Jue and Cui Gongzu lead lightly equipped shield-and-spear troops quickly from river reeds while a few boats carry food jars and sacks alongshore. Make the tactical cue visually legible without captions. The defenders scramble from meals; no beheading, burning fort, giant assault towers or bodies.', '炊烟可见度、舟运位置与接近路线为视觉重构;叛军见台军烟火即力攻、竹里十二日陷有传记依据。'), sources: ['《南齐书·崔慧景传》卷五十一'] },
{ id: '03', title: '龙尾夜进·围逼台城', timePlace: '蒋山西岩、乐游苑与台城', purpose: '解释叛军如何绕开正面防线进入建康。', narration: '崔慧景沿蒋山龙尾鱼贯夜下,朝廷军惊散。叛军进入乐游苑,台城闭门。', overlay: { heading: '从山后入城', line: '真正打开通路的不是正面强攻,而是一条守军没来得及封住的山道。' }, people: [{ name: '万副儿', office: '竹里山民、猎户', action: '建议沿蒋山龙尾绕行' }, { name: '崔慧景', office: '叛军主帅', action: '采纳建议,率军夜进乐游苑' }, { name: '左兴盛', office: '右卫将军', action: '率朝廷军拒战而退' }], image: image('03-night-descent-from-jiangshan', '夜色蒋山狭窄龙尾山道,崔慧景军一列列持遮灯火把鱼贯下山,山脚朝廷营垒因侧后出现敌军而慌乱撤离;远处台城木门正在闭合。', 'On a narrow “Dragon Tail” path descending Jiangshan at night, Cui Huijing’s troops move single file with hooded lanterns, guided by local hunter Wan Fuer. Below, court positions break after seeing troops emerge from an unexpected flank; farther away Taicheng’s heavy timber gates close just in time. No supernatural stealth, glowing route line, modern map or full city panorama.', '龙尾路径、夜行灯具、台城距离与守军反应为视觉重构;万副儿献道、千余人鱼贯夜下和台城闭门有史载。'), sources: ['《南齐书·崔慧景传》卷五十一'] },
{ id: '04', title: '援军渡江·十二日兵散', timePlace: '采石、建康城外与蟹浦', purpose: '交代叛乱因错失阻渡和内部倒戈而失败。', narration: '萧懿援军由采石渡江,崔觉战败,崔恭祖倒戈。围城十二日后,崔慧景弃军逃亡。', overlay: { heading: '十二日而散', line: '他已经围住宫门,却把江面和部下都留给了对手。' }, people: [{ name: '萧懿', office: '豫州刺史、征虏将军', action: '率援军自采石渡江解围' }, { name: '崔觉', office: '叛军前锋', action: '渡淮迎战失败后退' }, { name: '崔恭祖', office: '叛军骁将', action: '与刘灵运转投朝廷' }, { name: '崔慧景', office: '叛军主帅', action: '率少数亲信逃向蟹浦,后被杀;不展示首级' }], image: image('04-relief-crosses-cui-flees-to-xiepu', '黎明建康水网,萧懿援军浅底船已在采石南岸登岸,台城方向鼓火回应;前景崔慧景仅带数骑沿蟹浦芦苇小道逃离,一名渔父在远处持篙旁观,不画杀戮或鱼篮首级。', 'At dawn in Jiankang’s waterways, Xiao Yi’s relief troops disembark from shallow boats after crossing at Caishi while signal fires answer from Taicheng. In the foreground Cui Huijing flees with only a few mounted followers along a reed-lined path toward Xiepu; a distant fisherman with a pole watches from a skiff, foreshadowing the recorded end without depicting killing or a severed head. No triumphal tableau.', '采石与蟹浦压缩同框、援军登岸和渔父远影为视觉并置;援军渡江、崔觉败、部将降和崔慧景逃亡被杀有史载。'), sources: ['《南齐书·崔慧景传》卷五十一'] },
],
bgm: { catalogId: 'zhanzhen:beijingbaoweizhan#0', title: '北京保卫战', version: '电影级配乐·都城攻守结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删除京剧人声和明代音色,只取低弦、鼓点与守城压力层;广陵回师冷起,龙尾夜进加速,援军渡江后立即拆散节拍;删除歌词和品牌尾签。', reason: '项目暂无崔慧景专曲,借用项目自生成的都城攻守结构,但明确剥离原曲人物与时代声部;仓库暂无成品音频。', cues: [cue('01','00:00','00:21','回军决定以低弦起。'),cue('02','00:21','00:42','炊烟战术加入短促打击。'),cue('03','00:42','01:05','夜进与闭门推至主峰。'),cue('04','01:05','01:27','援军登岸后节拍瓦解。')] },
sources: [{ claim: '崔慧景回军、沿江进攻、龙尾夜进、围城与败亡', citation: '《南齐书·崔慧景传》卷五十一', url: 'https://zh.wikisource.org/zh-hans/南齐书/卷051' }, { claim: '永元二年崔慧景举兵袭京师的本纪纪年', citation: '《南齐书·东昏侯纪》卷七', url: 'https://zh.wikisource.org/zh-hans/南齐书/卷007' }],
blacklist: ['把崔慧景写成奉诏直接入京,省略广陵回军反叛', '把十二日围城画成长年围困或台城已经被攻陷', '省略萧懿援军、崔觉败与崔恭祖倒戈,只写渔父偶然杀主帅', '展示断首、鱼篮首级或夸张处刑', '现代文字、水印、后世宫城服甲、火器、尸堆与血腥'],
},
{
dir: '北魏/参合陂之战(衔枚夜袭与坑降)',
event: { canonicalName: '参合陂之战(衔枚夜袭与坑降)', aliases: ['参合陂之战', '参合陂之役', '北魏破后燕'], projectDynasty: '北魏', era: '北魏登国十年、后燕建兴十年', date: '公元395年十一月', place: '黄河以北参合陂一带', summary: '后燕太子慕容宝伐魏后撤至参合陂,未能建立有效殿后;拓跋珪率军衔枚夜进,黎明突袭使燕军崩溃,俘获大批将吏。拓跋珪采纳王建建议坑杀多数俘虏,事后悔恨。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '登国十年,后燕太子慕容宝率大军进攻北魏。拓跋珪避开正面决战,向西转移,使燕军长期隔河等待。军粮将尽,慕容宝决定退兵,却没有建立可靠的殿后与警戒。燕军退到参合陂时,忽遇大风和昏暗尘雾;僧人支昙猛提醒魏军可能追来,建议严设后卫,意见没有真正落实。拓跋珪则率轻军渡河,命士卒衔枚,连夜逼近。黎明前后,魏军从高地和营后突然出现,刚从睡梦中惊起的燕军无法列阵,三军迅速崩溃。慕容宝等只带少数骑兵逃走,陈留王慕容绍战死,大批文武将吏被俘。战斗之后,拓跋珪原想挑选有才能者留下,其余发给衣粮遣返;将领王建坚持认为放归俘虏会留下后患,最终多数俘虏被坑杀。史书紧接着写“太祖既而悔焉”。参合陂让后燕元气大伤,北魏由此获得席卷河北的主动权;它既是一场成功的夜袭,也是一场不能被胜负叙事遮住的战后暴行。' },
scenes: [
{ id: '01', title: '隔河避锋·燕军退师', timePlace: '黄河与参合陂道路', purpose: '建立拓跋珪避战诱敌和燕军粮尽后撤。', narration: '拓跋珪西移避锋,后燕军隔河久待。粮运不继,慕容宝下令退向参合陂。', overlay: { heading: '先让开正面', line: '北魏没有迎着八万大军硬撞,而是让时间和粮道先替自己作战。' }, people: [{ name: '拓跋珪', office: '魏王、北魏太祖', action: '避开燕军锋芒并等待追击机会' }, { name: '慕容宝', office: '后燕太子', action: '统军伐魏,粮尽后撤' }, { name: '后燕转运军', office: '粮运与车队', action: '在寒地长路上补给困难' }], image: image('01-yan-army-withdraws-from-yellow-river', '初冬黄河北岸,后燕长列车骑正在撤离渡口,空粮车与疲惫驮马居中;远处西岸北魏轻骑只在高地观察,没有正面决战。', 'Early winter north of the Yellow River. A long but plausible Later Yan column withdraws from a river camp with nearly empty supply carts and tired pack animals. Crown prince Murong Bao rides near the center while distant Northern Wei light cavalry observe from western high ground without attacking. Emphasize logistics, separation and the decision to retreat, not a giant pitched battle.', '粮车状态、双方距离和撤军队形为视觉推演;拓跋珪避锋、燕军粮尽退还有史载。'), sources: ['《魏书·太祖纪》卷二', '《晋书·慕容垂载记》卷一百二十三'] },
{ id: '02', title: '风尘覆营·警告不纳', timePlace: '参合陂后燕宿营地', purpose: '表现自然风尘与警戒失误,不神化为天象预言。', narration: '燕军驻于参合陂,大风卷起黑色尘幕。支昙猛请设强力殿军,警告没有落实。', overlay: { heading: '风不是神谕', line: '真正致命的不是黑气,而是所有人都以为追兵还很远。' }, people: [{ name: '支昙猛', office: '僧人、随军者', action: '提醒魏军将至并建议设殿' }, { name: '慕容宝', office: '后燕太子', action: '未采纳足够严密的警戒方案' }, { name: '慕容麟', office: '后燕宗王', action: '名义殿后却未形成可靠防线' }], image: image('02-warning-in-dusty-yan-camp', '参合陂傍晚后燕营地,大风卷起真实沙尘形成暗墙,支昙猛在军帐外指向营后空旷高地劝谏,慕容宝与将领看向地图木案;巡骑稀疏,不画妖云或神迹。', 'At dusk in the Later Yan camp beside Shenhe Slope, a natural wall of windblown dark dust crosses the plain. Monk Zhi Tanmeng urgently points toward unguarded rear high ground while Murong Bao and commanders stand over blank route tallies. Only a sparse patrol is visible. Treat the “black qi” as weather and poor visibility, never a supernatural omen, demon cloud or prophecy scene.', '尘雾形态、军帐会议和警戒空缺为视觉重构;大风黑气、支昙猛谏防魏师而不纳有史载。'), sources: ['《晋书·慕容垂载记》卷一百二十三'] },
{ id: '03', title: '衔枚夜进·拂晓营溃', timePlace: '参合陂燕军营后高地', purpose: '还原夜袭突然而非神兵天降。', narration: '北魏军衔枚连夜逼近。拂晓从营后突现,燕军来不及列阵,三军奔溃。', overlay: { heading: '天亮时已在背后', line: '夜里没有喊杀,等第一声惊呼传开,退路已经变成了乱军。' }, people: [{ name: '拓跋珪', office: '北魏魏王', action: '率军渡河并组织衔枚夜进' }, { name: '北魏诸部骑兵', office: '追击部队', action: '包裹马蹄、保持安静接近营后' }, { name: '慕容宝', office: '后燕太子', action: '在突袭中率少数骑兵逃脱' }], image: image('03-wei-silent-night-attack-at-dawn', '黎明灰光下,北魏轻骑从参合陂营后高地安静展开,部分马蹄裹布、士卒口衔小枚;燕军帐篷区刚有人惊醒奔出,尚未形成阵列,不画睡梦屠杀和尸体。', 'In the gray light before sunrise, Northern Wei light cavalry deploy from high ground behind the Later Yan camp after a silent night march. Some horse hooves are wrapped and soldiers visibly carry small silence-gags at the mouth. Below, startled Yan troops emerge from tents unable to form ranks as Murong Bao’s mounted guard searches for an exit. Show surprise and collapse without slaughtering sleeping men, bodies or gore.', '马蹄包裹、衔枚方式、进攻高地与营中反应为视觉重构;魏军衔枚夜进、拂晓大破燕军有史载。'), sources: ['《魏书·太祖纪》卷二', '《晋书·慕容垂载记》卷一百二十三'] },
{ id: '04', title: '俘众待决·坑降而悔', timePlace: '参合陂战后北魏军营', purpose: '明确记录战后暴行,并避免直接展示杀戮。', narration: '魏军俘获数千将吏。王建坚持坑杀多数俘虏,拓跋珪事后悔恨。', overlay: { heading: '胜利之后的命令', line: '战场已经结束,最沉重的决定却在俘虏放下武器之后才发生。' }, people: [{ name: '拓跋珪', office: '北魏魏王', action: '在遣返与处死俘虏之间作出决定,后来悔恨' }, { name: '王建', office: '北魏将领', action: '坚持坑杀俘虏以绝后患' }, { name: '后燕俘虏', office: '将吏与军士', action: '分区等候处置;不直接展示杀害' }], image: image('04-tuoba-gui-decides-fate-of-captives', '战后暮色北魏军营,成排武器甲胄已被收缴,后燕俘虏坐在远处分区等待;前景王建在无字木牍旁坚持进言,拓跋珪背对俘虏神情迟疑,地平线只有未开挖的空土坡,不画坑杀过程。', 'At dusk after the battle, rows of surrendered Later Yan armor and weapons lie under guard while captives sit in separated distant groups. In the foreground Wang Jian argues beside blank wooden tallies; Tuoba Gui looks back in visible conflict. An untouched earthen slope on the horizon foreshadows the recorded mass execution without showing a pit, killing, bodies or restraints as spectacle. Keep the moral weight in the decision.', '军营议决、俘虏分区、收缴军械与空土坡为视觉转译;王建固执坑降、拓跋珪后来悔恨见《魏书》列传。'), sources: ['《魏书·王建传》卷三十', '《魏书·太祖纪》卷二'] },
],
bgm: { catalogId: 'zhanzhen:canhebei#0', title: '参合陂之战', version: '电影级配乐·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '撤军以稀疏低鼓起,风尘不作神异音效;衔枚段抽空高频,拂晓只爆发一次,战后议决立即降为单音与风声;删除歌词和品牌尾签。', reason: '项目已有参合陂专属电影级配乐,叙事结构与本包完全对应;仓库暂无成品音频。', cues: [cue('01','00:00','00:21','粮尽退军的低鼓。'),cue('02','00:21','00:42','风尘与空警戒收窄声场。'),cue('03','00:42','01:04','拂晓夜袭唯一爆发。'),cue('04','01:04','01:26','战后议决冷却至单音。')] },
sources: [{ claim: '北魏渡河、至参合陂大破燕军及所获俘虏军资', citation: '《魏书·太祖纪》卷二', url: 'https://zh.wikisource.org/zh-hans/魏书/卷002' }, { claim: '大风黑气、警告不纳与燕军奔溃', citation: '《晋书·慕容垂载记》卷一百二十三', url: 'https://zh.wikisource.org/zh-hans/晋书/卷123' }, { claim: '王建坚持坑俘与拓跋珪事后悔恨', citation: '《魏书·王建传》卷三十', url: 'https://zh.wikisource.org/zh-hans/魏书/卷030' }],
blacklist: ['把大风黑气画成妖云、神迹或决定胜负的超自然力量', '把衔枚夜袭画成北魏凭空传送或燕军毫无撤军背景', '回避坑降事实,或反过来直接展示群体处决取乐', '把拓跋珪塑造成当场坚决主张坑杀,忽略王建进言与其后悔', '现代文字、水印、蒙古帝国或清代服甲、火器、尸堆与血腥'],
},
{
dir: '北魏/六镇之乱(沃野起兵)',
event: { canonicalName: '六镇之乱(沃野起兵)', aliases: ['六镇之乱', '沃野镇起兵', '破落汗拔陵之乱'], projectDynasty: '北魏', era: '北魏孝明帝正光四年至孝昌年间', date: '《魏书》系于公元524年三月,通鉴考异认为起事始于523年冬', place: '沃野、柔玄、怀荒等北方军镇并向河北扩展', summary: '北魏迁都洛阳后,北方六镇军户地位与供给日益恶化;沃野镇人破落汗拔陵聚众杀镇将起兵,乱势随后扩展到柔玄、上谷与河北,成为北魏政治军事秩序崩解的开端。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '北魏迁都洛阳以前,平城以北的六镇守着柔然方向,是王朝最重要的边防。迁都以后,朝廷中心远离北边,许多镇兵从开国集团的“国之肺腑”逐渐变成身份低下、供给不足的府户。正光年间,边地饥困与军镇矛盾一起加深。按照《魏书》帝纪的记载,正光五年三月,沃野镇人破落汗拔陵聚众反叛,杀死镇将,自号真王;《资治通鉴》考异则把起事开端放在前一年冬天,素材应保留这个纪年差异。起兵不是六座城在同一天同时爆炸。沃野之后,柔玄镇的杜洛周在上谷聚众,五原降户鲜于修礼又在定州起兵,饥民、镇兵和流亡者不断汇入不同队伍。洛阳朝廷起初把它当成边地军乱,镇压与招抚反复失效。大量人口向南流动,新的军事集团也在平乱过程中形成。六镇之乱没有一个单独的决战终点,却改变了整个北方权力结构;尔朱荣、高欢和宇文泰所处的新秩序,都从这场崩解中出现,北魏最终分裂为东魏与西魏。' },
scenes: [
{ id: '01', title: '迁洛之后·边镇被遗忘', timePlace: '沃野镇与通往洛阳的驿路', purpose: '用可见的供给断裂解释起兵背景,并标明属于综合重构。', narration: '迁都洛阳后,六镇仍守北边,镇兵身份和供给却不断下降。边防与朝廷渐成两个世界。', overlay: { heading: '留下来的人', line: '城墙没有南迁,守墙的人却从王朝核心变成了洛阳眼里的边户。' }, people: [{ name: '六镇府户', office: '世袭镇兵与家属', action: '修墙、领粮并等待长期拖欠的补给' }, { name: '镇将与戍主', office: '北魏边镇官', action: '以不足物资维持防线' }, { name: '洛阳使者', office: '朝廷驿使', action: '携空白文书沿漫长驿路来去' }], image: image('01-neglected-woye-garrison', '北魏沃野夯土军镇,镇兵和家属在风雪中修补木栅,粮仓只剩少量袋粮;远处一名洛阳驿使离城南去,城内仍挂无字旧旗,不画悲情乞丐。', 'At the rammed-earth Woye garrison in a bitter northern wind, hereditary frontier soldiers and families repair timber palisades beside a nearly depleted but orderly granary. A lone Luoyang courier departs south on the long road. The garrison remains functional yet neglected, with blank worn standards. Show institutional distance, not a literal class diagram, starving caricatures or modern poverty imagery.', '粮仓余量、修墙场景和孤独驿使为背景综合重构;迁洛后六镇地位下降与边地困敝为项目史实解释。'), sources: ['《魏书·肃宗纪》卷九', '《资治通鉴》卷一百五十'] },
{ id: '02', title: '沃野起兵·镇门易手', timePlace: '沃野镇内外', purpose: '呈现起兵与镇将被杀,但避开直接暴力。', narration: '破落汗拔陵在沃野聚众起兵,镇将被杀。边镇的兵第一次把武器转向镇内。', overlay: { heading: '沃野先反', line: '这不是外敌攻城,而是守城的人先拒绝继续替这套秩序守下去。' }, people: [{ name: '破落汗拔陵', office: '沃野镇人', action: '聚众起兵并自号真王' }, { name: '沃野镇兵', office: '府户与戍卒', action: '控制军械库和镇门' }, { name: '沃野镇将', office: '北魏镇将', action: '在兵变中被杀;不展示遗体或具体死状' }], image: image('02-baling-rebels-take-woye-gate', '暮色沃野镇门,破落汗拔陵站在普通镇兵之间指挥关闭城门,戍卒接管无字旗、弓弩和粮库钥匙;镇将空置的坐骑被牵到一旁,暗示权力易手,不画杀人。', "At dusk inside Woye’s gate, Poliuhan Baling stands among ordinary frontier soldiers as they close the timber doors and take control of blank standards, crossbows and granary keys. The former commander's riderless horse is led aside, implying the recorded killing without showing it. This is an internal mutiny in a working garrison, not an outside barbarian storming a Chinese city.", '军械库、钥匙、无主坐骑与镇门控制为视觉转译;破落汗拔陵聚众反、杀镇将有帝纪记载。'), sources: ['《魏书·肃宗纪》卷九', '《北史·魏本纪》卷四'] },
{ id: '03', title: '乱火沿镇南下', timePlace: '柔玄、上谷、定州道路', purpose: '表现不同起兵力量先后出现,避免画成统一军团。', narration: '沃野之后,杜洛周起于柔玄,鲜于修礼又反于定州。镇兵、饥民和流亡者分路南下。', overlay: { heading: '不是一支军队', line: '同一种裂缝,在不同军镇长出不同首领,最后一起压向河北。' }, people: [{ name: '杜洛周', office: '柔玄镇人', action: '率众反于上谷、围攻燕州' }, { name: '鲜于修礼', office: '五原降户', action: '在定州聚众起兵' }, { name: '六镇流民', office: '镇户与饥民', action: '携家南下,被不同军队吸收' }], image: image('03-separate-rebel-columns-move-south', '北方旷野的三个相隔很远的道路纵深,分别有不同服色但同属北魏边镇的队伍南下,中间夹杂推车家庭和步行流民;各队旗帜无字且彼此不连接,避免画成整齐统一大军。', 'Across a vast northern landscape, three widely separated roads carry distinct frontier columns south at different depths. Their equipment is related but their blank standards and formations differ; between them civilian families push carts and walk with bundles. Represent Baling, Du Luozhou and Xianyu Xiuli as successive, separate movements, never one synchronized national army or a literal map with arrows.', '三路同框、服色差异与流民队伍为时间压缩式视觉推演;多镇起兵先后发生并向河北扩展有史载。'), sources: ['《魏书·肃宗纪》卷九'] },
{ id: '04', title: '洛阳迟报·分裂伏线', timePlace: '洛阳朝堂与北方流民营', purpose: '说明六镇之乱的长期结果,而不把后续河阴之变提前画进来。', narration: '洛阳不断收到边报,镇压和招抚仍未止乱。流民南下,新的军阀集团从平乱中形成。', overlay: { heading: '边乱成为天下乱', line: '朝廷以为失去的是六座军镇,后来才发现失去的是整个北方秩序。' }, people: [{ name: '北魏朝臣', office: '洛阳中枢官员', action: '阅读多份无字边报,争论镇压与招抚' }, { name: '流民与降户', office: '向河北迁徙者', action: '在临时营地等待安置' }, { name: '后起军事集团', office: '平乱军中的将领与部曲', action: '只以远景整队暗示,不点名预演河阴屠杀' }], image: image('04-luoyang-receives-frontier-reports', '洛阳朴素尚书省内,朝臣面对堆叠无字木牍争论,窗外远处临时流民营和正在整队的新募军同处灰尘中;不画尔朱荣屠河阴或东西魏旗号,只留秩序将变的暗示。', 'Inside a restrained Northern Wei Luoyang government hall, officials debate over stacked blank frontier reports. Through the open court beyond, a temporary refugee camp and newly recruited units form in separate dusty spaces. Suggest that suppression is creating new military power without depicting the later Heyin massacre, naming future factions on flags or using a supernatural time portal.', '朝堂与流民营同框、新募军整队和边报堆叠为结果性视觉推演;六镇乱扩大、流民南下并促成新军事集团属后续史实。'), sources: ['《魏书·肃宗纪》卷九', '《资治通鉴》卷一百五十'] },
],
bgm: { catalogId: 'mingchangmian:heyinzhibian#0', title: '河阴之变·沉太后于河', version: '电影级配乐·因果前奏段借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '只取原曲尚未进入河阴屠杀的低频前奏,沃野夺门加一层木鼓,多路南下形成复调,洛阳边报处不进入屠杀高潮;删除人声、歌词和品牌尾签。', reason: '项目暂无六镇专曲,而河阴曲在项目叙事中正是六镇乱四年后的直接回声;本包只借其前奏,明确不提前混画河阴事件;仓库暂无成品音频。', cues: [cue('01','00:00','00:21','边镇风声与稀疏低频。'),cue('02','00:21','00:42','镇门易手加入木鼓。'),cue('03','00:42','01:05','多路南下形成错位复调。'),cue('04','01:05','01:26','边报堆叠,以未解决和弦收尾。')] },
sources: [{ claim: '破落汗拔陵于沃野起兵、杀镇将及后续诸镇起兵', citation: '《魏书·肃宗纪》卷九', url: 'https://zh.wikisource.org/zh-hans/魏书/卷009' }, { claim: '沃野起兵的另一帝纪见证', citation: '《北史·魏本纪》卷四', url: 'https://zh.wikisource.org/zh-hans/北史/卷004' }, { claim: '起事纪年考异与乱势扩展', citation: '《资治通鉴》卷一百五十', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷150' }],
blacklist: ['把六镇画成六座城在同一天由同一支军队同时起兵', '把迁都与起兵写成单一直接因果,忽略长期身份、供给与边地危机', '把破落汗拔陵、杜洛周、鲜于修礼合并成一人或统一指挥', '提前把河阴之变、东西魏分裂画成本次起兵现场', '现代文字、水印、蒙古帝国或清代服甲、火器、尸堆与血腥'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((person) => `${person.name}(${person.office};${person.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((item) => `| ${item.from}–${item.to} | ${item.scene} | ${item.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Liu Song, Southern Qi and Northern Wei core battle packages`)
+289
View File
@@ -0,0 +1,289 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材', '曹魏')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed late Eastern Han material culture, cinematic 16:9 landscape, subtle film grain, physically plausible scale, rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered iron lamellar armor, straight swords, crossbows, spears, ji halberds, cavalry tack and wooden carts or river boats. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, samurai armor, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({
original: `images/${file}.png`,
delivery: `images/${file}.webp`,
alt,
prompt: `Use case: historical-scene. Digital Me Cao Wei project story frame, set before the formal Wei founding and therefore rendered as late Eastern Han. ${request} ${BASE}`,
visualInference: inference,
refinementPrompt: '',
})
const packs = [
{
dir: '潼关之战(抹书离间)',
event: {
canonicalName: '潼关之战(抹书离间)',
aliases: ['潼关之战', '渭南之战', '曹操破马超韩遂'],
projectDynasty: '曹魏',
era: '东汉献帝建安十六年(项目按曹魏线归类)',
date: '公元211年七月至九月',
place: '潼关、蒲阪津、黄河与渭南',
summary: '关中诸将因疑惧而联合屯兵潼关;曹操先坚壁、遣徐晃朱灵夜渡蒲阪,再以丁斐放牛马解北渡之急,最终采贾诩离间之计,使马超与韩遂相疑后以虎骑夹击取胜。',
},
narration: {
language: 'zh-CN',
estimatedSeconds: 87,
fullText: '建安十六年,曹操准备经关中进攻张鲁。关中诸将怀疑这是假道袭己,马超、韩遂等十部遂联合据守潼关。关西兵精悍、善用长矛,曹操命诸将坚壁不战,暗遣徐晃、朱灵夜渡蒲阪,在黄河西岸建立营地。随后曹操亲自从潼关以北渡河,马超骑兵突至,箭下如雨。校尉丁斐放出牛马,关西兵争取牲畜,曹军才完成渡河。曹操沿河修筑甬道,进到渭南,与联军隔阵相持。马超、韩遂屡次挑战,又请求割地、送任子求和。曹操采纳贾诩建议,表面答应。韩遂请求单独会面,曹操只同他谈京城旧事,隔阵谈笑;随后寄去一封多处涂改的信,仿佛韩遂自己修改过。马超果然更加怀疑韩遂。待联盟互不信任,曹操约期会战,先用轻兵交锋,战斗良久后突然投入虎骑从两翼夹击,联军大败,马超、韩遂退往凉州。项目把此役归入曹魏线,但战役发生时曹操仍是东汉丞相,不能提前画成魏帝。',
},
scenes: [
{
id: '01', title: '十部屯潼关', timePlace: '公元211年七月,潼关以西', purpose: '建立关中联军形成、曹操拒绝正面硬撼的开局。',
narration: '马超、韩遂等关中诸将因疑惧而联合屯潼关。曹操判断关西兵精悍,下令坚壁勿战。',
overlay: { heading: '十部屯关', line: '最危险的不是一支军队,而是一群因共同恐惧暂时结盟的人。' },
people: [
{ name: '曹操', office: '东汉丞相、曹军主帅', action: '判断关西兵精悍,命诸将坚壁勿战' },
{ name: '马超、韩遂', office: '关中联军主要将领', action: '与杨秋、李堪、成宜等屯据潼关' },
],
image: image('01-coalition-holds-tong-pass', '潼关外黄土峡谷中,马超韩遂联军以骑兵和长矛阵守住关口,远处曹操在低矮土垒后约束诸将不出战。', 'Late-summer Loess Plateau at Tong Pass. In the middle distance, the temporary coalition of Ma Chao and Han Sui holds the pass with separate clusters of cavalry and long-spear infantry, their equipment similar but banners intentionally varied and blank to show a coalition rather than one uniform army. Ma Chao is a vigorous East Asian commander in his mid-30s with tied hair, close moustache and practical pale-gray lamellar armor; Han Sui is an older, weathered commander in his late 50s in dark brown armor. Across a dry ravine, Cao Cao—mid-50s, compact build, sharp eyes, short moustache and beard, practical dark red-brown lamellar armor—stands behind low earthworks, restraining eager officers from attacking. No regal costume.', '联军分区、阵距、人物外貌和曹军营垒为视觉重构;十部并叛、屯潼关及曹操命坚壁勿战有史载。'),
sources: ['《三国志·魏书·武帝纪》卷一', '《三国志·蜀书·马超传》卷三十六'],
},
{
id: '02', title: '蒲阪夜渡', timePlace: '八月,蒲阪津与黄河西岸', purpose: '呈现先遣部队建立西岸桥头,而非全军直接从潼关正面突破。',
narration: '徐晃、朱灵夜渡蒲阪,在河西先立营垒。曹军由此绕开潼关正面。',
overlay: { heading: '蒲阪夜渡', line: '先把一支小军送到河对岸,整条防线就不再只剩潼关。' },
people: [
{ name: '徐晃、朱灵', office: '曹军将领', action: '率部潜夜渡蒲阪津,据河西为营' },
{ name: '曹操', office: '东汉丞相', action: '以先遣营地改变渡河条件' },
],
image: image('02-night-crossing-at-puban', '月夜蒲阪津,徐晃朱灵的小队用木筏和浅底船分批渡过黄河,在西岸悄然树立低矮木栅;东岸主力营火被山体遮蔽。', 'Moonlit Yellow River at Puban ford. Xu Huang, a sturdy East Asian field commander in his 40s with square jaw and dark practical armor, and Zhu Ling, a lean commander of similar age, supervise compact infantry units crossing quietly in shallow wooden boats and lashed rafts. On the western bank the first troops establish a low timber-and-earth bridgehead while scouts watch the dark ridges. Main Cao campfires remain mostly hidden behind terrain. Human-scale secret movement, no enormous pontoon bridge and no combat in the water.', '具体舟筏、渡点光线、桥头营形制与人物外貌为视觉重构;徐晃朱灵夜渡蒲阪津、据河西为营有史载。'),
sources: ['《三国志·魏书·武帝纪》卷一'],
},
{
id: '03', title: '牛马解渡河之急', timePlace: '黄河北渡途中', purpose: '表现曹操北渡受袭与丁斐放牛马打乱追兵的应急节点。',
narration: '曹操北渡未完,马超骑兵急攻。丁斐放出牛马,追兵争取牲畜,曹军得以渡河。',
overlay: { heading: '牛马解围', line: '最紧迫的渡河时刻,救命的不是奇兵,而是一群被放开的牲畜。' },
people: [
{ name: '丁斐', office: '校尉', action: '放出牛马吸引关西兵争取' },
{ name: '曹操', office: '东汉丞相', action: '在箭雨压力下完成北渡' },
{ name: '马超', office: '关中联军将领', action: '率骑兵急击尚未渡完的曹军' },
],
image: image('03-livestock-breaks-the-river-attack', '黄河浅滩渡河紧急,曹操部队推船登岸,马超骑兵从沙地逼近;丁斐命人打开围栏,牛马横穿两军之间使追兵队形散开。', 'Daylight emergency at a broad Yellow River shallows. Cao Cao and his escort are still unloading from simple ferry boats under distant arrow pressure as Ma Chao’s cavalry approaches across sandy ground. At the crucial moment Ding Fei, an East Asian officer in plain dark armor, orders a rough livestock corral opened; loose cattle and horses stream across the space between armies, causing parts of the pursuing coalition cavalry to turn and lose formation while Cao troops haul the last boats ashore. Tactical confusion without slapstick, no animals injured and no gore.', '牲畜数量、围栏位置、渡船形制、箭雨密度及追兵转向为视觉重构;马超赴船急战、丁斐放牛马、贼乱取而曹操得渡有史载。'),
sources: ['《三国志·魏书·武帝纪》卷一'],
},
{
id: '04', title: '交马语旧·抹书生疑', timePlace: '渭南两军阵前与曹军营帐', purpose: '把离间拆成公开交谈与后续涂改书信两个连续动作。',
narration: '韩遂单独会曹操,两人只谈京城旧事。随后一封多处涂改的信送到韩遂营中,马超疑心更重。',
overlay: { heading: '抹书生疑', line: '信里没有密约;真正起作用的,是让人以为密约被擦掉了。' },
people: [
{ name: '曹操', office: '东汉丞相', action: '与韩遂交马谈旧,并寄出多处点窜的书信' },
{ name: '韩遂', office: '关中联军将领', action: '隔阵会谈后接收曹操书信' },
{ name: '马超', office: '关中联军将领', action: '因会谈与点窜书信更加怀疑韩遂' },
{ name: '贾诩', office: '曹操谋士', action: '提出伪许求和、离间联盟' },
],
image: image('04-old-talk-and-altered-letter', '渭南两阵之间,曹操与韩遂各骑马隔着护从谈旧,远处马超观察;前景以军吏呈上一封无可读文字但有明显涂改痕迹的木牍暗示第二步。', 'Layered late-afternoon composition on the Weinan plain. In the middle distance Cao Cao and Han Sui remain mounted with a restrained gap between small escorts, speaking casually about old acquaintances rather than negotiating theatrically; their hands are relaxed. Farther back Ma Chao watches with visible suspicion from his own separate group. In the foreground inside Cao’s field shelter, Jia Xu—a thin elderly East Asian adviser in plain dark robes—and a clerk prepare blank wooden slips bearing visible scraping and ink-smudge corrections but absolutely no readable characters. No secret handshake, no shouting match.', '会谈距离、护从人数、马超观察位置、书牍形式和同框结构为视觉重构;交马语旧、拊手欢笑、多所点窜及马超愈疑韩遂有史载。'),
sources: ['《三国志·魏书·武帝纪》卷一', '《三国志·魏书·荀彧荀攸贾诩传》卷十'],
},
{
id: '05', title: '虎骑夹击', timePlace: '九月,渭南会战', purpose: '以轻兵接战、久战后投入虎骑夹击收束,不画夸张单挑。',
narration: '联盟彼此猜疑后,曹操约期会战。轻兵相持良久,虎骑忽从两翼投入,关中联军崩解。',
overlay: { heading: '虎骑夹击', line: '离间先拆掉彼此的信任,骑兵才有机会拆掉他们的阵线。' },
people: [
{ name: '曹操', office: '曹军主帅', action: '先以轻兵接战,后投入虎骑夹击' },
{ name: '马超、韩遂', office: '关中联军将领', action: '各部在猜疑中应战,败退凉州' },
{ name: '虎骑', office: '曹军精锐骑兵', action: '在战斗持续后从两翼夹击' },
],
image: image('05-tiger-cavalry-flank-attack', '渭南黄尘战场,曹军轻兵在中央稳住相持,虎骑从两个可读的侧翼同时切入;马超韩遂各自部曲之间出现空隙后撤,无单挑与斩首。', 'Dusty Weinan battlefield in early autumn. Cao light infantry holds a measured central engagement while two clearly readable wings of elite Tiger Cavalry sweep around the coalition flanks only after the struggle has lasted. Ma Chao’s and Han Sui’s previously separate formations lose coordination, leaving a widening gap as officers try to withdraw their own units. Cao Cao observes from a protected command position, not charging alone. Emphasize timing, mistrust and formation collapse; no duel, beheading, gore or giant cavalry wall.', '虎骑规模、两翼路线、阵线缺口与主帅位置为视觉重构;克日会战、先以轻兵挑战、战良久后纵虎骑夹击并大破有史载。'),
sources: ['《三国志·魏书·武帝纪》卷一', '《三国志·魏书·荀彧荀攸贾诩传》卷十'],
},
],
bgm: {
catalogId: 'zhanzhen:tongguan#0', title: '潼关之战', version: '曹操版·建议使用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '前两幕用低频鼓点表现拒战与暗渡,第三幕短促打乱节奏,第四幕降到木牍摩擦与低弦,末幕才释放骑兵夹击高潮;删除歌词与品牌尾签。',
reason: '项目已有潼关战役条目,优先复用同事件自生成音乐;仓库暂无可直接交付的成品音频。',
cues: [
{ from: '00:00', to: '00:18', scene: '01', direction: '压住正面冲突,突出坚壁。' },
{ from: '00:18', to: '00:35', scene: '02', direction: '以稀疏脉冲表现夜渡。' },
{ from: '00:35', to: '00:52', scene: '03', direction: '牲畜放出时出现短暂节奏错位。' },
{ from: '00:52', to: '01:10', scene: '04', direction: '收窄为低弦和木质敲击。' },
{ from: '01:10', to: '01:27', scene: '05', direction: '虎骑夹击形成唯一高潮。' },
],
},
sources: [
{ claim: '关中诸将屯潼关、蒲阪夜渡、丁斐放牛马、交马语旧、抹书离间与虎骑夹击', citation: '《三国志·魏书·武帝纪》卷一', url: 'https://zh.wikisource.org/zh-hans/三国志/卷01' },
{ claim: '贾诩建议伪许求和并以离间破马超韩遂', citation: '《三国志·魏书·荀彧荀攸贾诩传》卷十', url: 'https://zh.wikisource.org/zh-hans/三国志/卷10' },
{ claim: '马超一方参战、败退及后续经历', citation: '《三国志·蜀书·马超传》卷三十六', url: 'https://zh.wikisource.org/zh-hans/三国志/卷36' },
],
blacklist: [
'把211年的曹操画成魏帝;此时仍为东汉丞相,项目只是按曹魏线归类',
'加入许褚裸衣斗马超、曹操割须弃袍等《三国演义》桥段',
'把抹书离间误画成伪造一封可读密约;正史重点是多处点窜,引发猜疑',
'把关中联军画成服饰旗号完全一致的单一国家军队',
'表现斩首、尸堆、牲畜受伤或大规模血腥近景',
'现代文字、水印、后世服饰建筑、火器爆炸或动漫画风',
],
},
{
dir: '合肥之战(张辽八百突阵)',
event: {
canonicalName: '合肥之战(张辽八百突阵)',
aliases: ['合肥之战', '逍遥津之战', '张辽威震逍遥津'],
projectDynasty: '曹魏',
era: '东汉献帝建安二十年(项目按曹魏线归类)',
date: '公元215年八月',
place: '合肥城下与撤军津渡',
summary: '孙权率十万围合肥,张辽、李典按曹操密教以八百敢死士清晨突阵,折其锐气后退守;孙权十余日不能拔城,撤退时又遭追击,几乎被擒。',
},
narration: {
language: 'zh-CN',
estimatedSeconds: 86,
fullText: '建安二十年,曹操远征张鲁,只留下张辽、乐进、李典等七千余人驻守合肥,并把一封密教交给护军薛悌,封面只注明敌军到来才开启。八月,孙权果然率十万众围城。诸将拆开教令:若孙权到,张辽、李典出战,乐进守城,护军不得参战。众人疑惑,以为兵力相差太大。张辽却说,援军抵达前城池可能早已攻破,必须趁吴军尚未合围,先折其盛势。李典放下旧怨,与他同意。当天夜里,张辽募集八百敢战士卒,宰牛犒军。第二天清晨,他披甲持戟,率队直冲吴军营阵,一直突到孙权麾下。孙权退上高冢,以长戟自守。张辽突围后听见被围部众呼救,又返身冲入,把余众带出。战至中午,吴军士气受挫。孙权围城十余日仍不能攻克,只得撤军。张辽随后追击,在津渡附近几乎擒获孙权。项目把此役归入曹魏线,但战役发生时曹魏尚未正式建国;“跃马断桥”和“张辽止啼”都不是陈寿正文锁定的战役节点。',
},
scenes: [
{
id: '01', title: '贼至乃发', timePlace: '合肥城内,孙权军抵达后', purpose: '用密教分工建立守军为何选择主动出战。',
narration: '孙权十万围城,薛悌开启曹操留下的密教。张辽、李典出战,乐进守城。',
overlay: { heading: '贼至乃发', line: '七千守军等到十万敌军出现,才知道各自该站在哪里。' },
people: [
{ name: '张辽、李典', office: '合肥守将', action: '按密教准备出战' },
{ name: '乐进', office: '合肥守将', action: '按密教留守城池' },
{ name: '薛悌', office: '护军', action: '保存并开启曹操所留密教' },
{ name: '孙权', office: '江东主帅', action: '率大军围合肥' },
],
image: image('01-sealed-orders-opened', '合肥城楼下的简陋议事处,薛悌打开封闭木函,张辽李典乐进围看无字木牍;城外平原上孙权营阵正在展开。', 'Inside a modest command room beneath Hefei’s rammed-earth wall, Protector Xue Ti opens a sealed wooden case and lays out blank bamboo orders before Zhang Liao, Li Dian and Yue Jin. Zhang Liao is a powerful East Asian commander in his mid-40s with broad cheekbones, short beard and dark iron lamellar armor; Li Dian is leaner in brown armor, Yue Jin compact in muted black-red armor. Through the open gate tower, Sun Quan’s much larger army can be seen establishing separate camps across the plain. Tense division of duties, no readable writing and no palace.', '木函与议事位置、三将外貌、城外营势及开启方式为视觉重构;曹操留密教、贼至乃发与张李出战乐进守城有史载。'),
sources: ['《三国志·魏书·张辽传》卷十七'],
},
{
id: '02', title: '夜募八百', timePlace: '合肥城内,出战前夜', purpose: '表现敢战士卒的筛选、犒军和明日任务,不画血誓。',
narration: '张辽夜募敢战之士八百人,宰牛犒军。所有人都知道,天亮后要先冲出去。',
overlay: { heading: '夜募八百', line: '他们不是全部守军,而是替七千人争取军心的一支突击队。' },
people: [
{ name: '张辽', office: '守将、突击主将', action: '夜募八百敢战士卒并部署清晨出击' },
{ name: '李典', office: '守将', action: '与张辽同意先发折敌盛势' },
{ name: '八百敢战士卒', office: '突击队', action: '受犒后整备甲胄戈戟' },
],
image: image('02-eight-hundred-volunteers-at-night', '合肥城内夜色中,八百敢战士卒分队领取食物、检查甲胄戈戟,张辽李典在灯下指明城门与突击路线;只暗示宰牛犒军,不展示屠宰。', 'Night inside Hefei. Selected assault soldiers assemble in several human-scale groups, eating from shared bowls, tightening armor, checking ji halberds, shields and crossbows. A butchered ox is implied only by covered meat baskets and steaming cooking pots, with no slaughter shown. Under shielded lamps Zhang Liao and Li Dian mark a gate and assault lanes using stones and cord on a plain board without text. The mood is focused and sober, not a blood oath or drunken feast.', '士卒编组、犒军器物、路线推演和灯光为视觉重构;夜募敢从之士八百、椎牛飨士、明日大战有史载。'),
sources: ['《三国志·魏书·张辽传》卷十七', '《三国志·魏书·李典传》卷十八'],
},
{
id: '03', title: '平旦陷阵', timePlace: '清晨,孙权围城营阵', purpose: '表现张辽突击的组织性和直抵孙权麾下,不做个人无双。',
narration: '清晨,张辽披甲持戟先登,八百人沿窄正面冲入营阵,直抵孙权麾下。',
overlay: { heading: '平旦陷阵', line: '八百人要做的不是歼敌十万,而是在十万人站稳之前先夺走他们的气势。' },
people: [
{ name: '张辽', office: '突击主将', action: '披甲持戟率队冲阵,大呼自名' },
{ name: '孙权', office: '江东主帅', action: '遇突击后退上高冢,以长戟自守' },
{ name: '八百敢战士卒', office: '合肥突击队', action: '随张辽穿透尚未严整的围城营阵' },
],
image: image('03-dawn-assault-reaches-sun-quan', '薄雾清晨,张辽与密集小队冲破吴军一处营门向高冢推进,孙权在护卫簇拥下登上土丘持长戟防守;不画张辽一人屠阵。', 'Mist-gray dawn outside Hefei. Zhang Liao, matching scene 01, leads a compact wedge of shielded assault troops through one disrupted gate lane of the still-forming Wu siege camp, advancing toward a low earthen rise. Sun Quan, early 30s with short beard, practical dark green-brown lamellar armor and no imperial robe, withdraws with a small guard onto the mound and holds a long ji defensively. Surrounding Wu units turn in surprise but remain numerous. Show the eight hundred as coordinated squads, not one invincible superhero; no visible killing or gore.', '突击路线、营门、土丘位置、人物外貌和吴军反应为视觉重构;张辽被甲持戟、先登陷阵、冲至孙权麾下及权登高冢自守有史载。'),
sources: ['《三国志·魏书·张辽传》卷十七'],
},
{
id: '04', title: '复还拔众', timePlace: '清晨至中午,吴军营阵内外', purpose: '强调张辽已突围后返回救出被围部众的第二次突入。',
narration: '张辽先带数十人冲出,听见余众呼救,又返身突入重围,把他们带了出来。',
overlay: { heading: '复还拔众', line: '最能稳住军心的,不是冲出去,而是冲出去以后又回来。' },
people: [
{ name: '张辽', office: '突击主将', action: '突围后复返,救出被围部众' },
{ name: '被围突击队', office: '八百敢战士卒', action: '在吴军合围中保持小型防御圈' },
{ name: '吴军', office: '孙权围城部队', action: '试图重新合围并修复营阵' },
],
image: image('04-zhang-liao-returns-for-his-men', '吴军营垒出口形成两向动作:已出阵的张辽率数十人转身再入,内侧被围士卒结成小圆阵向外接应;无血腥。', 'A clear two-direction composition at the broken edge of the Wu siege camp. Zhang Liao and several dozen troops who have reached open ground turn back into the breach, shields overlapping, while the remaining Hefei assault soldiers hold a compact defensive ring inside and move toward them. Wu troops attempt to close the corridor from both sides but hesitate under the renewed push. Late-morning hard light, dust and torn blank cloth screens, disciplined rescue rather than slaughter. No bodies, gore, severed weapons or superhero leap.', '两次突围的空间压缩、被围圆阵、接应走廊与光线为视觉推演;余众呼救、张辽复还突围并拔出余众、自旦战至日中有史载。'),
sources: ['《三国志·魏书·张辽传》卷十七'],
},
{
id: '05', title: '十余日不拔·津渡追击', timePlace: '围城十余日后,合肥外津渡', purpose: '以孙权撤军和张辽追击收束,同时剔除跃马断桥的锁定画面。',
narration: '孙权围城十余日不能攻克,只得撤军。张辽率诸军追击,在津渡附近几乎擒获孙权。',
overlay: { heading: '津渡追击', line: '守城已经成功;最后一次出击,差一点改变江东的继承线。' },
people: [
{ name: '孙权', office: '江东主帅', action: '攻城不拔后分批撤向津渡' },
{ name: '张辽', office: '合肥守将', action: '率诸军追击,几乎擒获孙权' },
{ name: '李典、乐进', office: '合肥守将', action: '与守军协同保城并追击退军' },
],
image: image('05-pursuit-at-the-river-crossing', '阴天津渡,孙权与近卫分批登上低矮渡船,后队在木桥和浅滩撤离;远处张辽部队追到但尚有距离,不画跃马飞越断桥。', 'Overcast river crossing after more than ten days of failed siege. Sun Quan and a reduced guard move onto low ferry boats while withdrawing Wu units file across a practical timber landing and shallow causeway. In the distance Zhang Liao’s pursuing troops emerge from dust and reeds, close enough to create danger but not yet physically capturing the ruler. Several abandoned carts show the haste of withdrawal. Deliberately avoid the later dramatic image of a horse leaping a broken bridge; no collapsed span, no drowning soldiers and no gore.', '津渡形制、撤军批次、双方距离和辎重遗留为视觉重构;孙权攻城不拔引退、张辽率军追击几复获权有史载,跃马过桥仅为异说。'),
sources: ['《三国志·魏书·张辽传》卷十七', '《三国志·吴书·吴主传》卷四十七'],
},
],
bgm: {
catalogId: 'mingchangmian:xiaoyaojin#0', title: '合肥·逍遥津(张辽威震江东)', version: '八百突击版·建议使用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '第一幕由封函敲击启动,第二幕压低为整备节奏,第三幕和第四幕形成连续但有回返感的突击高潮,第五幕以追击鼓点后冷收;删除歌词与品牌尾签。',
reason: '项目名场面库已有逍遥津专属自生成音乐,叙事视角与本包一致;仓库暂无可直接交付的成品音频。',
cues: [
{ from: '00:00', to: '00:17', scene: '01', direction: '封函开启后进入低鼓。' },
{ from: '00:17', to: '00:34', scene: '02', direction: '保持克制的整备节拍。' },
{ from: '00:34', to: '00:52', scene: '03', direction: '清晨突阵第一次抬升。' },
{ from: '00:52', to: '01:09', scene: '04', direction: '短暂回落后随复返再起。' },
{ from: '01:09', to: '01:26', scene: '05', direction: '追至津渡后冷静收束。' },
],
},
sources: [
{ claim: '曹操密教、八百突击、孙权登高、张辽复返救众、攻城不拔与追击几获孙权', citation: '《三国志·魏书·张辽传》卷十七', url: 'https://zh.wikisource.org/zh-hans/三国志/卷17' },
{ claim: '李典与张辽在危局中协力拒敌', citation: '《三国志·魏书·李典传》卷十八', url: 'https://zh.wikisource.org/zh-hans/三国志/卷18' },
{ claim: '孙权方面的合肥进军与撤退纪年', citation: '《三国志·吴书·吴主传》卷四十七', url: 'https://zh.wikisource.org/zh-hans/三国志/卷47' },
],
blacklist: [
'把215年的合肥守军画成已经正式建国的曹魏帝国军;项目只是按曹魏线归类',
'把八百突击画成张辽单人屠杀十万人的无双场面',
'把跃马飞越逍遥津断桥当作陈寿正文锁定史实;本包不采用该异说画面',
'混入甘宁百骑劫营、凌统救主等其他战事与他传情节',
'采用张辽止啼作为真实战役节点或加入儿童恐惧镜头',
'表现斩将、尸堆、溺亡、血腥近景或宰牛过程',
'现代文字、水印、后世服饰建筑、火器爆炸或动漫画风',
],
},
]
function markdown(pack) {
const lines = [
`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '',
`- 规范事件名:${pack.event.canonicalName}`,
`- 常用称呼:${pack.event.aliases.join('、')}`,
`- 项目归类:${pack.event.projectDynasty}`,
`- 真实时代:${pack.event.era}`,
`- 日期:${pack.event.date}`,
`- 地点:${pack.event.place}`,
`- 一句话结论:${pack.event.summary}`, '',
'## 60–90 秒主旁白', '', pack.narration.fullText, '',
'## 分幕素材', '',
]
for (const scene of pack.scenes) {
lines.push(
`### ${scene.id}|${scene.title}`, '',
`- 时间地点:${scene.timePlace}`,
`- 本幕作用:${scene.purpose}`,
`- 分幕旁白:${scene.narration}`,
`- 页面标题:${scene.overlay.heading}`,
`- 页面短句:${scene.overlay.line}`,
`- PNG:\`${scene.image.original}\``,
`- WebP:\`${scene.image.delivery}\``,
`- 替代文本:${scene.image.alt}`,
`- 视觉推演边界:${scene.image.visualInference}`,
`- 人物:${scene.people.map((person) => `${person.name}(${person.office};${person.action})`).join(';')}`,
`- 史料:${scene.sources.join(';')}`, '',
'生成提示词:', '', '```text', scene.image.prompt, '```', '',
)
}
lines.push(
'## BGM 选曲', '',
`- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`,
`- 状态:${pack.bgm.status}`,
`- 用法:${pack.bgm.usage}`,
`- 选择理由:${pack.bgm.reason}`,
'- 音频路径或 URL:仓库暂缺,待导出', '',
'| 时间 | 分幕 | 编排 |', '|---|---|---|',
...pack.bgm.cues.map((cue) => `| ${cue.from}–${cue.to} | ${cue.scene} | ${cue.direction} |`), '',
'## 史料来源', '',
...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '',
'## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '',
'## 交付检查', '',
'- [ ] 每幕 PNG 原图存在且为 16:9',
'- [ ] 每幕 WebP 交付图存在且为 16:9',
'- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全',
'- [ ] BGM 已导出并填写可用路径',
'- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '',
)
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
}
console.log(`Generated ${packs.length} Cao Wei core battle packages`)
@@ -0,0 +1,74 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材', '西晋')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed late Three Kingdoms and Western Jin material culture, cinematic 16:9 landscape, subtle film grain, physically plausible scale, rammed earth, aged timber, hemp, leather, low-sheen silk, lacquered iron lamellar armor, straight swords, crossbows, spears, ji halberds, cavalry tack, wooden carts and river craft. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Wu and Western Jin story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const packs = [
{
dir: '晋灭吴之战(王濬楼船下益州)',
event: { canonicalName: '晋灭吴之战(王濬楼船下益州)', aliases: ['晋灭吴之战', '王濬伐吴', '三国归晋'], projectDynasty: '孙吴/西晋', era: '西晋武帝太康元年、孙吴天纪四年', date: '公元280年正月至三月', place: '长江上游至建业', summary: '晋军分六路伐吴,王濬率楼船从益州顺江而下,以火炬熔断江中铁锁、连破沿江守备,先抵建业受孙皓之降,三国割据至此结束。' },
narration: { language: 'zh-CN', estimatedSeconds: 88, fullText: '太康元年,晋武帝下令六路伐吴。西线最关键的一支来自益州:龙骧将军王濬多年造船练水军,率大舰顺长江东下。吴军曾在江面横设铁锁,又把铁锥沉入水中阻船。晋军预先制作大木筏扫开水下铁锥,再以装满油料的火炬贴近铁锁,烧熔锁链,打开航道。王濬沿江攻克西陵、夷道、乐乡,吴将或死或降。吴丞相张悌没有固守建业,而是渡江迎击晋军别路,战败死节;吴的野战主力由此失去最后屏障。三月,王濬船队首先抵达建业。孙皓采纳薛莹、胡冲的建议,分送降书,亲自反绑双手、载着棺木出降。王濬为他解缚、焚去棺木,接受投降。正史并不支持一艘巨舰单独灭国:胜利来自多路牵制、上游水军突破和吴国内部长期衰败。东吴四主五十九年国祚终结,西晋完成统一;后来刘禹锡写下“王濬楼船下益州”,把这场沿江推进留在了诗里。' },
scenes: [
{ id: '01', title: '益州造舰·六路并进', timePlace: '益州长江船场与晋军各路出发地', purpose: '建立长期造舰准备和六路协同,而非临时造出神舰。', narration: '王濬在益州训练水军、整备楼船。晋军受命分六路同时向吴境推进。', overlay: { heading: '上游起舰', line: '一支船队从益州出发,背后是数年的造船与一整张多路进军图。' }, people: [{ name: '王濬', office: '龙骧将军、益州刺史', action: '督造战船并率上游水军东下' }, { name: '晋武帝司马炎', office: '西晋皇帝', action: '定六路伐吴之议;不与前线同框近景' }], image: image('01-fleet-leaves-yizhou', '益州江边大型木质船场,王濬在岸上检视多层甲板战船与水军登船,远处不同旗色的小队沿陆路出发,旗面无字。', 'At a broad upper-Yangtze shipyard, Wang Jun, about 74, tall, gray-bearded and energetic despite age, inspects large but physically plausible timber warships with layered fighting decks while trained marines board. Carpenters finish rigging and oars rather than magically assembling ships. In distant valleys separate Jin columns depart on land, suggesting six coordinated routes without a map graphic. Muted winter light, no impossible skyscraper ships.', '楼船具体尺度、船场布局、各路同框与人物外貌为视觉重构;王濬在益州造船练水军、晋六路伐吴有史载。'), sources: ['《晋书·武帝纪》卷三', '《晋书·王濬传》卷四十二'] },
{ id: '02', title: '木筏扫锥·火炬熔锁', timePlace: '长江峡口吴军封锁线', purpose: '解释晋水军如何突破铁锥与铁锁。', narration: '晋军先用大木筏扫开水下铁锥,再以油炬烧熔横江铁锁。', overlay: { heading: '破锁开江', line: '障碍藏在水下,破解也不是蛮撞:先扫锥,再熔锁。' }, people: [{ name: '王濬', office: '晋水军主帅', action: '采纳预制木筏与火炬破障方案' }, { name: '晋军舟师与工匠', office: '水军、船工', action: '操筏清障并在安全距离处理铁锁' }], image: image('02-rafts-clear-spikes-and-burn-chains', '峡江封锁线上,大木筏在前承受并拔起水下铁锥,后方船工用长杆推出燃烧油炬贴近粗铁锁,主舰保持距离,不画爆炸。', 'In a narrow Yangtze gorge, broad sacrificial timber rafts move first and snag concealed iron spikes, several bent points emerging above the water. Behind them engineers on low boats push long-handled bundles of oil-soaked reeds toward a heavy chain stretched between anchor points, heating links with controlled flame while the main fleet waits upstream. Smoke, current and practical teamwork; no explosive fireballs, burning people or impossible molten river.', '铁锥密度、锁链锚点、筏与火炬操作细节为视觉重构;作大筏清铁锥、以火炬烧熔铁锁有史载。'), sources: ['《晋书·王濬传》卷四十二'] },
{ id: '03', title: '沿江连破·乐乡失守', timePlace: '西陵、夷道至乐乡沿江防线', purpose: '呈现突破后吴军防区接连失守,但不做一路屠杀。', narration: '航道打开后,王濬连克西陵、夷道与乐乡。吴军据点相继被切断。', overlay: { heading: '顺流而下', line: '封锁线一破,上游每一处孤立守备都开始面对同一支船队。' }, people: [{ name: '王濬', office: '龙骧将军', action: '率水军顺流推进、受降沿江守军' }, { name: '孙歆等吴将', office: '吴沿江守将', action: '分守各处,部众在孤立中败退或投降' }], image: image('03-river-defenses-collapse', '长江宽阔江面,晋楼船纵队从已拆开的木栅缺口通过,岸上吴军小寨降下无字旗并开门,下一处烽台仍在远方示警。', 'A disciplined column of Jin river warships passes through a dismantled timber boom into a wider reach. On one bank a modest Wu riverside fort opens its gate and lowers a blank standard while guards surrender weapons in ordered piles; farther downstream another beacon tower warns the next position. Wang Jun observes from a practical command deck. Show cascading isolation and capitulation rather than a burning-city montage or bodies.', '多个据点压缩同框、投降礼节、船队数量与烽台距离为视觉推演;晋军连克西陵、夷道、乐乡有史载。'), sources: ['《晋书·武帝纪》卷三', '《晋书·王濬传》卷四十二'] },
{ id: '04', title: '张悌渡江·吴军最后一战', timePlace: '牛渚附近江北岸', purpose: '交代吴军主力为何在王濬抵达前已失去屏障。', narration: '吴丞相张悌渡江迎战晋军别路,兵败死节,建业再无完整野战军可用。', overlay: { heading: '江北死战', line: '张悌知道大势已去,仍把吴军最后一阵摆在建业之外。' }, people: [{ name: '张悌', office: '吴丞相、军师', action: '率军渡江迎击,战败死节;不展示遗体' }, { name: '沈莹、诸葛靓', office: '吴将', action: '随军作战;诸葛靓力劝撤退未果' }, { name: '王浑', office: '晋安东将军', action: '统江北晋军与吴军交战' }], image: image('04-zhang-ti-last-stand', '冬末江北滩地,张悌率吴军结成最后的盾矛阵,诸葛靓回头示意退路,晋军在远处展开;不画将领死亡、尸堆或决斗。', 'On a cold north-bank floodplain near Niuzhu, Wu chancellor Zhang Ti, around 40, scholar-general in restrained dark red-brown armor, holds the final compact shield-and-spear formation. Zhuge Jing gestures toward a viable withdrawal path while Zhang Ti turns back toward the advancing Jin line. Wang Hun’s force deploys at a distance. The tension is moral and strategic, not a duel; no death pose, blood or heaps of fallen soldiers.', '阵形、两将对话姿态、交战距离与天气为视觉重构;张悌渡江迎战、兵败死节且诸葛靓劝退有史载。'), sources: ['《三国志·吴书·孙皓传》卷四十八', '《晋书·武帝纪》卷三'] },
{ id: '05', title: '建业出降·三国归晋', timePlace: '建业城外长江岸,公元280年三月', purpose: '用受降而非屠城收束统一。', narration: '王濬最先抵达建业。孙皓自缚舆榇出降,王濬解缚焚榇,东吴灭亡。', overlay: { heading: '解缚焚榇', line: '楼船停在石头城下,五十九年的东吴以一场受降结束。' }, people: [{ name: '孙皓', office: '吴主', action: '自缚、舆榇出降;保持完整衣着与非羞辱视角' }, { name: '王濬', office: '晋龙骧将军', action: '受降并命人解缚焚榇' }, { name: '薛莹、胡冲', office: '吴光禄勋、中书令', action: '参与议降与草降书' }], image: image('05-sun-hao-surrenders-at-jianye', '建业低矮城门与江岸之间,孙皓双手用白绳松缚、身旁有空木棺,王濬下船命人解绳并移走棺木,楼船停泊背景,无屈辱裸身。', 'Between Jianye’s restrained rammed-earth gate and the river landing, Sun Hao approaches in plain court robes with his hands loosely bound in white cord, an empty wooden coffin cart beside the small delegation. Elderly Wang Jun has stepped down from his command ship and gestures for attendants to cut the cord and carry the coffin toward a controlled fire. Warships rest behind without storming the city. Dignified surrender, no naked humiliation, kneeling spectacle or palace fantasy.', '城门、受降站位、绳缚、棺车和焚榇动作均为视觉重构;王濬先至、受孙皓降、解缚焚榇有史载。'), sources: ['《三国志·吴书·孙皓传》卷四十八', '《晋书·武帝纪》卷三', '《晋书·王濬传》卷四十二'] },
],
bgm: { catalogId: 'shici:tang-x234', title: '西塞山怀古 · 刘禹锡', version: '化用歌·仅用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '前两幕用低编钟与战鼓推进,破锁时到达主峰;第三四幕逐渐抽空,第五幕转入箫与江风,不做统一凯歌;删除歌词和品牌尾签。', reason: '项目曲目本身即以王濬楼船、铁锁沉江和石头城降幡为结构,是此役最直接的项目内音乐锚点;仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:18', scene: '01', direction: '低编钟与船场木声起。' }, { from: '00:18', to: '00:36', scene: '02', direction: '破锁形成唯一重奏。' }, { from: '00:36', to: '00:53', scene: '03', direction: '顺流推进但降低胜利感。' }, { from: '00:53', to: '01:10', scene: '04', direction: '抽去铜管,留下低弦。' }, { from: '01:10', to: '01:28', scene: '05', direction: '受降后转入箫与江风。' }] },
sources: [{ claim: '六路伐吴、张悌败亡、三月平吴', citation: '《晋书·武帝纪》卷三', url: 'https://zh.wikisource.org/zh-hans/晋书/卷003' }, { claim: '王濬造舰、木筏清锥、火炬熔锁与顺江推进', citation: '《晋书·王濬传》卷四十二', url: 'https://zh.wikisource.org/zh-hans/晋书/卷042' }, { claim: '孙皓分送降书、王濬先至并解缚焚榇', citation: '《三国志·吴书·孙皓传》卷四十八', url: 'https://zh.wikisource.org/zh-hans/三国志/卷48' }],
blacklist: ['把晋灭吴简化成王濬一艘楼船独力灭国,忽略六路协同', '把铁锁写成炸药炸断,或加入火炮、火药与蒸汽机关', '把张悌死节画成与王濬阵前单挑', '把孙皓出降做成裸身羞辱、屠城或血腥示众', '现代文字、水印、后世服饰建筑或动漫奇幻风'],
},
{
dir: '荡阴之战(嵇绍死驾)',
event: { canonicalName: '荡阴之战(嵇绍死驾)', aliases: ['荡阴之战', '成都王颖败晋惠帝', '嵇绍死驾'], projectDynasty: '西晋', era: '西晋惠帝永兴元年', date: '公元304年七月', place: '荡阴(今河南汤阴一带)', summary: '八王之乱中,东海王司马越奉晋惠帝亲征成都王司马颖;荡阴一战朝廷军溃败,惠帝中三箭被俘,侍中嵇绍以身护驾而死。' },
narration: { language: 'zh-CN', estimatedSeconds: 83, fullText: '永兴元年,八王之乱已经把西晋朝廷撕成数个互相征伐的集团。东海王司马越挟晋惠帝北征邺城,讨伐掌兵的成都王司马颖。皇帝亲临并没有让这支朝廷军变得更稳。两军在荡阴相遇,司马颖部将石超突然进击,护驾军阵迅速瓦解。百官和将士争相逃散,惠帝的乘舆被留在乱军中。皇帝面颊受伤,身中三箭,随从几乎散尽。侍中嵇绍端正朝服,始终站在御车之前。敌军拉他离开,惠帝也说这是忠臣,不要杀;乱兵却没有停手。嵇绍在车前遇害,血溅皇帝衣服。战后侍从要洗去血迹,惠帝制止,说这是嵇侍中的血,不要洗。司马颖军俘获惠帝送往邺城,西晋出现东西两个政令中心。此役没有赢家:诸王争权让皇帝成为随军的旗号和可争夺的俘虏,洛阳中枢进一步空洞化,数年后中原便滑向永嘉之乱。' },
scenes: [
{ id: '01', title: '奉帝北征', timePlace: '洛阳至荡阴行军途中', purpose: '交代皇帝被带入诸王内战。', narration: '司马越奉晋惠帝北征司马颖,百官宿卫随乘舆向邺城推进。', overlay: { heading: '天子随军', line: '皇帝成了这支军队最大的旗号,也成了败阵时最危险的目标。' }, people: [{ name: '晋惠帝司马衷', office: '西晋皇帝', action: '乘简朴御车随军北进' }, { name: '司马越', office: '东海王', action: '组织讨伐司马颖的朝廷军' }, { name: '嵇绍', office: '侍中', action: '着朝服随侍御驾' }], image: image('01-emperor-marches-north', '平原驿道上,简朴御车位于庞杂行军队列中央,司马越在前调度,嵇绍穿深色朝服骑行近侧,士卒与官员队形松散。', 'On a dusty North China road, the Western Jin emperor rides in a restrained timber carriage within a large but uneven column of guards, officials and supply carts. Sima Yue directs movement from the front while Ji Shao, early 50s, composed, thin mustache, formal dark court robe and tall black cap rather than fantasy armor, stays close to the carriage. The column already feels politically assembled rather than tactically unified.', '御车形制、队伍规模、官军混编与人物外貌为视觉重构;司马越奉帝北征、嵇绍随驾有史载。'), sources: ['《晋书·惠帝纪》卷四', '《晋书·忠义传·嵇绍》卷八十九'] },
{ id: '02', title: '石超突击·护驾军溃', timePlace: '荡阴平原', purpose: '呈现突然进击导致指挥和护驾体系断裂。', narration: '石超率司马颖军突然进击。朝廷军阵列迅速松动,百官宿卫四散。', overlay: { heading: '一击阵散', line: '拼凑起来的亲征大军,在突击前先暴露了自己的指挥裂缝。' }, people: [{ name: '石超', office: '成都王颖部将', action: '率军突击朝廷军' }, { name: '司马越', office: '东海王', action: '败阵后脱离战场' }, { name: '晋军宿卫', office: '护驾军', action: '阵形崩解,无法维持御车周边防线' }], image: image('02-shi-chao-breaks-the-line', '荡阴开阔平原,石超骑兵与步卒从侧前方冲击,朝廷军盾矛阵出现断口,官车和辎重阻塞退路,御车仍在中景。', 'On the open plain at Dangyin, Shi Chao’s compact cavalry and infantry strike the front-quarter of the court army. A visible gap opens in shield-and-spear ranks while official carriages and supply wagons clog the retreat lanes; the imperial carriage remains isolated in the middle distance. Dust, confusion and broken command rather than spectacular slaughter. No cloned cavalry wall or heroic single combat.', '突击方向、阵线断口、车辆阻塞与军种为战术视觉推演;石超来战、王师败绩有史载。'), sources: ['《晋书·惠帝纪》卷四'] },
{ id: '03', title: '乘舆陷阵·帝中三箭', timePlace: '荡阴战场中央', purpose: '交代皇帝受伤被困,但避免伤口特写。', narration: '护卫散去,惠帝乘舆陷入乱军。皇帝面部受伤,身中三箭。', overlay: { heading: '御驾失守', line: '当护驾军散去,天下至尊也只是留在战场中央的一辆车。' }, people: [{ name: '晋惠帝', office: '皇帝', action: '乘舆被围并受伤;箭伤只以破损护甲暗示' }, { name: '嵇绍', office: '侍中', action: '走到御车前保护皇帝' }], image: image('03-imperial-carriage-isolated', '尘雾战场中御车倾斜停住,车侧护盾插有三支箭而非人体中箭,惠帝由少数内侍扶坐,嵇绍步行挡在车前。', 'The imperial carriage has stopped at an angle amid dust, most guards gone. Three arrows are lodged visibly in the carriage shield and torn outer padding as a non-graphic symbol of the emperor’s recorded wounds; the emperor sits supported by two attendants with a clean bandage at the cheek. Ji Shao steps in front in formal court dress, unarmored and upright. Hostile troops close at a distance, no embedded arrows in flesh or blood.', '以车盾中箭替代人体伤口、御车状态与侍从人数为视觉转译;惠帝中三矢、百官奔散、嵇绍护驾有史载。'), sources: ['《晋书·惠帝纪》卷四', '《晋书·忠义传·嵇绍》卷八十九'] },
{ id: '04', title: '嵇绍端冕·死于帝侧', timePlace: '御车前', purpose: '表现护驾选择,不直接展示杀害。', narration: '嵇绍端正朝服护在车前。惠帝请求不要杀他,乱兵仍将他拖离。', overlay: { heading: '侍中在此', line: '所有人都在离开御车时,嵇绍把自己的位置留在了皇帝身前。' }, people: [{ name: '嵇绍', office: '侍中', action: '端冕侍侧、以身卫帝;死亡留在画外' }, { name: '晋惠帝', office: '皇帝', action: '制止士兵伤害嵇绍而未能成功' }, { name: '司马颖军士', office: '胜方士卒', action: '将嵇绍拉离御车;不展示兵刃入体' }], image: image('04-ji-shao-guards-the-carriage', '嵇绍以深色朝服正立御车前,惠帝从车内抬手制止,几名敌兵握住嵇绍衣袖向旁拉开,兵器压低,死亡不入画。', 'Ji Shao stands directly before the carriage in immaculate dark court robes and ceremonial cap, calm and unarmed. From inside, the wounded emperor reaches out with an urgent stop gesture. Several opposing soldiers grip Ji Shao’s sleeves and shoulders, pulling him away while their weapons remain lowered and no strike is shown. Frame the ethical decision and helpless command, with the fatal outcome kept entirely off-screen.', '拉离动作、士兵位置和帝王手势为视觉重构;嵇绍端冕以身卫帝、帝称忠臣勿杀而终遇害有史载。'), sources: ['《晋书·忠义传·嵇绍》卷八十九'] },
{ id: '05', title: '血衣不洗·帝送邺城', timePlace: '战后御车与北上道路', purpose: '以皇帝保留血衣和被送往邺城收束。', narration: '战后侍从欲洗御衣,惠帝说这是嵇侍中的血,不要洗。随后他被送往邺城。', overlay: { heading: '此嵇侍中血', line: '战场已经过去,皇帝能保住的,只剩一件不许洗去的衣服。' }, people: [{ name: '晋惠帝', office: '被俘皇帝', action: '制止侍从清洗带有血痕的外袍' }, { name: '嵇绍', office: '侍中', action: '以空置冠帽和叠放外袍纪念,不展示遗体' }, { name: '司马颖军', office: '胜方军队', action: '护送或押送皇帝前往邺城' }], image: image('05-bloodstained-robe-not-washed', '暮色营地,惠帝按住侍从手中的一件带少量暗红痕迹的外袍,不让放入水盆;旁置嵇绍空冠,远处车队准备北行。', 'At a subdued dusk camp, the emperor places his hand over a folded outer robe with only a small dark-red stain, stopping an attendant from lowering it into a wash basin. Ji Shao’s empty black court cap rests beside the robe, no body present. Beyond the tent, a guarded carriage column prepares to move north toward Ye. Quiet memorial tone, minimal stain, no wound or corpse.', '水盆、叠衣、空冠与车队同框为视觉重构;帝称嵇侍中血勿洗、战后被送往邺城有史载。'), sources: ['《晋书·忠义传·嵇绍》卷八十九', '《晋书·惠帝纪》卷四'] },
],
bgm: { catalogId: 'nvxing:jiananfeng#0', title: '贾南风', version: '八王之乱末世器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '去除人物歌词,将其王朝下坠的暗潮用于前三幕;第四幕突然抽空,只留单弦;第五幕以低合唱和车轮声结束;删除品牌尾签。', reason: '项目内贾南风曲明确以八王之乱和西晋倾覆为历史后果,适合作为荡阴败局的同朝音乐母题;仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:16', scene: '01', direction: '低频行军,保持政治性不昂扬。' }, { from: '00:16', to: '00:33', scene: '02', direction: '突击时节奏失衡。' }, { from: '00:33', to: '00:49', scene: '03', direction: '御车孤立后逐层抽空。' }, { from: '00:49', to: '01:06', scene: '04', direction: '侍中护驾只留单弦。' }, { from: '01:06', to: '01:23', scene: '05', direction: '车轮低响收束。' }] },
sources: [{ claim: '荡阴败绩、惠帝受伤及被送邺城', citation: '《晋书·惠帝纪》卷四', url: 'https://zh.wikisource.org/zh-hans/晋书/卷004' }, { claim: '嵇绍端冕护驾、帝称忠臣、血衣不洗', citation: '《晋书·忠义传·嵇绍》卷八十九', url: 'https://zh.wikisource.org/zh-hans/晋书/卷089' }],
blacklist: ['把八王之乱写成固定两国之间的民族战争', '把嵇绍护驾改成持兵大杀四方或武将单挑', '直接展示惠帝中箭伤口、嵇绍被杀或血泊尸体', '把“血溅帝衣”夸张为全身血衣', '现代文字、水印、后世服饰建筑、火器或奇幻风'],
},
{
dir: '永嘉之乱(洛阳陷落)',
event: { canonicalName: '永嘉之乱(洛阳陷落)', aliases: ['永嘉之乱', '永嘉五年洛阳陷落', '刘曜攻陷洛阳'], projectDynasty: '西晋', era: '西晋怀帝永嘉五年', date: '公元311年三月至六月', place: '宁平城、洛阳与平阳', summary: '八王之乱后西晋中枢已空,石勒在宁平城歼灭东海王司马越护丧军,刘曜、王弥随后攻陷饥困洛阳,俘晋怀帝,西晋虽未当年灭亡却已失去首都与朝廷核心。' },
narration: { language: 'zh-CN', estimatedSeconds: 88, fullText: '永嘉五年,西晋并不是在一次城门失守时突然崩塌。八王之乱留下的军队和权力已经四分五裂。东海王司马越死后,王衍等人护送灵柩东行,十余万军民在宁平城附近被石勒追上。骑兵合围,护丧大军覆没,大批公卿同时遇害,中枢先被掏空一次。洛阳则长期缺粮,宫城内外饥困,守军既无援军,也无完整指挥。六月,汉赵将领刘曜、王弥攻入洛阳。宫庙被焚,官署遭到劫掠,晋怀帝在华林园被俘,送往平阳。正史记载百官士庶死者众多,但画面不把苦难变成尸体陈列:应看到的是空粮仓、烧毁的宫门、散失的簿籍和被押离都城的皇帝。西晋没有在311年立刻结束,长安仍另立愍帝;然而洛阳陷落意味着中央政权事实上断裂。大批士族与百姓继续南迁,六年后司马睿在建康建立东晋。从荡阴的皇帝被争夺,到永嘉的皇帝被俘,诸王内战最终把都城留给了外部军队。' },
scenes: [
{ id: '01', title: '司马越死·护丧东行', timePlace: '项县至宁平城道路', purpose: '交代洛阳陷落前中枢主力为何在外。', narration: '司马越死后,王衍率公卿军民护送灵柩东行,庞大队伍缓慢经过宁平。', overlay: { heading: '一辆丧车', line: '跟在灵柩之后的,不只是家属与士兵,而是西晋所剩的大半中枢。' }, people: [{ name: '王衍', office: '太尉', action: '率公卿与军民护送司马越灵柩东行' }, { name: '司马越', office: '东海王', action: '以封闭灵车与素幡表示,遗体不展示' }, { name: '石勒', office: '汉赵将领', action: '率骑兵在远处追踪护丧队伍' }], image: image('01-funeral-column-at-ningping', '灰白天空下,素幡无字的长队护送封闭灵车经过平原,官员、士卒、家眷和辎车混杂,远处地平线上出现石勒骑兵尘线。', 'Under an ash-gray sky, a vast but strung-out funeral column follows a closed timber hearse with blank white streamers. Senior officials, soldiers, families and overloaded carts intermingle, creating slow vulnerable depth. Wang Yan rides near the hearse, an elegant elderly statesman in dark robes; far on the horizon a thin dust line marks Shi Le’s cavalry scouting the column. No coffin interior, body or readable banner.', '队伍规模、家眷辎重混编、追兵距离与天气为视觉重构;王衍等奉司马越丧东行、石勒追击有史载。'), sources: ['《晋书·怀帝纪》卷五', '《晋书·王衍传》卷四十三'] },
{ id: '02', title: '宁平合围·中枢覆没', timePlace: '宁平城附近旷野', purpose: '表现护丧队伍被骑兵包围、朝臣系统性损失。', narration: '石勒骑兵追及并合围。护丧军无法展开,大批公卿在这一战同时遇害。', overlay: { heading: '宁平城败', line: '当车队堵住退路,十余万人的规模没有变成战力,只变成更大的困局。' }, people: [{ name: '石勒', office: '汉赵征东大将军', action: '指挥骑兵包围护丧队伍' }, { name: '王衍等西晋公卿', office: '护丧中枢', action: '被围后失去统一指挥;死亡不展示' }], image: image('02-cavalry-encircles-the-column', '宁平旷野,骑兵从两翼封闭道路,护丧车队和步卒被挤在车阵中,王衍与公卿在中央试图整队;不画箭雨杀人和尸堆。', 'On open ground near Ningping, mobile cavalry wings close the roads around a dense funeral and baggage column. Western Jin foot soldiers try to form shields between tangled carts while Wang Yan and other court officials gather around a central standard seeking orders. Show the tactical trap and immobilization from a high oblique view; weapons remain at engagement distance, with no arrow impacts, corpses or massacre spectacle.', '包围路线、车阵堵塞、公卿位置与俯视视角为战术推演;石勒追及宁平、晋军大败及公卿遇害有史载。'), sources: ['《晋书·怀帝纪》卷五', '《资治通鉴》卷八十七'] },
{ id: '03', title: '洛阳粮尽·孤城无援', timePlace: '洛阳城内,永嘉五年初夏', purpose: '建立陷城前的饥困与指挥真空。', narration: '宁平败讯传来,洛阳无援。粮仓几空,守军与百姓在残破城防间等待。', overlay: { heading: '都城已空', line: '城墙还在,中枢、军粮和援军却已经先一步消失。' }, people: [{ name: '晋怀帝司马炽', office: '西晋皇帝', action: '留守洛阳、等待无法到达的援军' }, { name: '刘默等', office: '河南尹与留守官员', action: '组织残余守军、分配有限粮食' }, { name: '洛阳军民', office: '守军与居民', action: '修补城门、凭空粮仓维持秩序' }], image: image('03-luoyang-starves-without-relief', '洛阳朴素夯土宫城内,打开的粮仓只余少量空筐,刘默组织军民修补木门,怀帝在远处台阶听取军报,街道无尸体。', 'Inside a restrained rammed-earth Luoyang compound, an open granary shows mostly empty baskets and swept floors. Henan governor Liu Mo organizes exhausted guards and civilians repairing a timber gate and rationing a few sacks. In the distance Emperor Huai listens to a messenger on plain palace steps with no lavish throne. Quiet hunger and institutional absence, no emaciated bodies, grand Forbidden City or melodramatic crowds.', '粮仓状态、修城动作、宫城形制与人员位置为视觉重构;洛阳饥困无援、留守官员在城有史载。'), sources: ['《晋书·怀帝纪》卷五'] },
{ id: '04', title: '刘曜入洛·宫庙焚毁', timePlace: '洛阳宫城,公元311年六月', purpose: '呈现城破与制度空间毁坏,避免屠杀猎奇。', narration: '刘曜、王弥攻入洛阳,宫庙与官署起火,残余守军失去抵抗。', overlay: { heading: '洛阳陷落', line: '火烧到宫门时,西晋失去的不只是一座城,还有朝廷继续运转的场所。' }, people: [{ name: '刘曜', office: '汉赵始安王', action: '率军攻入洛阳' }, { name: '王弥', office: '汉赵将领', action: '与刘曜部共同进入都城' }, { name: '刘默等留守官员', office: '西晋官员', action: '城破遇害;不展示死亡' }], image: image('04-luoyang-palace-gates-burn', '洛阳宫门一角燃烧,刘曜军穿过破损木门控制官署,士兵搬开武器与竹简,远处烟柱有限,不画屠杀。', 'A damaged timber gate of Luoyang’s modest palace precinct burns at one corner, contained flames and heavy smoke rather than a city-wide inferno. Liu Yao, a stern commander in mixed northern frontier lamellar armor, enters with disciplined troops who secure weapon racks and scattered bamboo documents. Civilians are guided away at a distance; abandoned seals and toppled screens indicate institutional collapse. No looting glamour, dead bodies or gore.', '宫门破损、火势、官署器物与军队控制流程为视觉重构;刘曜王弥入洛、焚烧宫庙有史载。'), sources: ['《晋书·怀帝纪》卷五', '《资治通鉴》卷八十七'] },
{ id: '05', title: '怀帝蒙尘·衣冠南渡', timePlace: '洛阳北出道路与黄河南岸', purpose: '以皇帝北押和人群南逃的相反方向收束。', narration: '晋怀帝被俘送往平阳。官民继续南迁,西晋残余则在长安另立朝廷。', overlay: { heading: '两条离城路', line: '皇帝被押向北,流民与士族渡河南下;洛阳之后,晋朝分成了两个方向。' }, people: [{ name: '晋怀帝', office: '被俘皇帝', action: '乘有守卫的普通车辆北送平阳' }, { name: '洛阳官民', office: '南迁人群', action: '携少量家物向南渡河' }, { name: '司马睿', office: '琅邪王', action: '作为江南政权承接者仅在文字标签中提及,不强行同框' }], image: image('05-emperor-north-refugees-south', '洛阳城外岔路,怀帝乘普通有篷车在骑兵护送下向北,另一支携竹箱与家眷的人群向南方渡口行进,残城在远方烟雾中。', 'At a fork outside ruined Luoyang, a guarded covered cart carries the captive Emperor Huai northward, dignified and not caged. In the opposite direction a smaller stream of officials, families and commoners carries bamboo cases, bedding and children toward a southern ferry. The damaged city recedes through limited smoke. Strong opposing diagonal paths convey political rupture without labels, maps or sentimental close-ups.', '岔路、押送车辆、南迁人群构成和方向对照为视觉重构;怀帝被俘送平阳、晋人南渡与后续建康中兴为史实链条。'), sources: ['《晋书·怀帝纪》卷五', '《晋书·元帝纪》卷六'] },
],
bgm: { catalogId: 'shici:weijin-x068', title: '桃花源诗 · 陶渊明', version: '秦乱避世母题·灾后器乐重编 stem', status: 'pending-export', pathOrUrl: '', usage: '反向使用项目内“避乱入山”母题:前两幕以无实鼓的低空气感写护丧覆没,第三四幕压入宫城火声,第五幕转为远箫和渡水声,不渲染灾难;删除歌词和品牌尾签。', reason: '项目没有永嘉专属战役曲;同属魏晋单元的桃花源曲以避乱、迁徙和朝代隔绝为核心,可克制承载衣冠南渡;仓库暂无成品音频。', cues: [{ from: '00:00', to: '00:18', scene: '01', direction: '素幡与车轮低声展开。' }, { from: '00:18', to: '00:35', scene: '02', direction: '合围只增加低频压力。' }, { from: '00:35', to: '00:53', scene: '03', direction: '空粮仓近乎无拍。' }, { from: '00:53', to: '01:11', scene: '04', direction: '宫门火声短暂压过乐器。' }, { from: '01:11', to: '01:28', scene: '05', direction: '远箫与渡水声消散。' }] },
sources: [{ claim: '宁平败、洛阳陷、怀帝被俘及西晋官民损失', citation: '《晋书·怀帝纪》卷五', url: 'https://zh.wikisource.org/zh-hans/晋书/卷005' }, { claim: '宁平城追击和洛阳陷落的日序', citation: '《资治通鉴》卷八十七', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷087' }, { claim: '晋室南渡与司马睿在建康承接政权', citation: '《晋书·元帝纪》卷六', url: 'https://zh.wikisource.org/zh-hans/晋书/卷006' }],
blacklist: ['把永嘉之乱简化为某一天突然亡国;西晋至316年才正式灭亡', '把不同族群画成妖魔化或统一异族符号', '用尸山血海表现正史伤亡数字', '把衣冠南渡写成所有北方人口一次整齐迁徙', '现代文字、水印、后世宫殿服饰、火器或奇幻风'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((person) => `${person.name}(${person.office};${person.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((cue) => `| ${cue.from}–${cue.to} | ${cue.scene} | ${cue.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Wu and Western Jin core battle packages`)
@@ -0,0 +1,83 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材')
const BASE = 'Premium live-action Chinese historical film still, archaeology-informed tenth-to-twelfth-century China, cinematic 16:9 landscape, subtle film grain, physically plausible troops, terrain, cities and logistics, rammed earth, aged timber, hemp, low-sheen silk, leather, iron lamellar armor, straight swords, bows, crossbows, spears, cavalry tack, wooden carts and period river craft. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, firearms, cannons, gunpowder explosions, European plate, magic, anime, gore or body piles.'
const image = (file, alt, request, inference) => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Digital Me Five Dynasties, Northern Song and Liao story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt: '' })
const person = (name, office, action) => ({ name, office, action })
const cue = (scene, from, to, direction) => ({ scene, from, to, direction })
const packs = [
{
dir: '五代/高平之战(周世宗亲征整军)',
event: { canonicalName: '高平之战(周世宗亲征整军)', aliases: ['高平之战', '显德高平之战', '周世宗破北汉'], projectDynasty: '五代', era: '后周世宗显德元年、北汉乾祐七年', date: '公元954年三月', place: '泽州高平、高原至巴公原一带', summary: '后周新君柴荣亲征南下的北汉与契丹援军,阵前虽有将领先遁,仍重整部队反败为胜,并追击北汉军;战后处死逃将、裁汰老弱,开启后周禁军整顿。' },
narration: { language: 'zh-CN', estimatedSeconds: 83, fullText: '显德元年,周太祖刚刚去世,北汉主刘旻便联合契丹军南下,试探新皇帝柴荣能否守住后周。柴荣没有留在开封等待,而是亲率禁军迎敌。两军在泽州高平一带相遇,北汉军先压住周军右翼。侍卫马军都指挥使樊爱能、步军都指挥使何徽临阵退却,一度带动部众奔逃。柴荣没有撤走,他在御阵前重整仍未溃散的军队,亲军和诸将重新向前,战局由危转胜。北汉军败退,周军追击到高平,再次击破敌军。战后,柴荣没有把胜利写成皆大欢喜。他处死樊爱能、何徽等逃将,又裁汰老弱、挑选精锐,重新训练禁军。高平之战首先保住了新君的皇位威望,更重要的是让后周开始拥有一支受中央控制、纪律更严的军队。几年以后,赵匡胤正是在这套军事基础上继续扩张,并建立北宋。' },
scenes: [
{ id: '01', title: '新君亲征·北汉南下', timePlace: '开封至泽州行军道', purpose: '建立周太祖新丧与北汉、契丹趁机南侵的背景。', narration: '柴荣即位不久便率军北上;刘旻联合契丹援军向泽州推进,双方在高平接近。', overlay: { heading: '新皇帝的第一仗', line: '北汉赌的是后周主少国疑,柴荣押上的却是刚刚坐稳的皇位。' }, people: [person('柴荣', '后周世宗', '亲率禁军北上迎敌'), person('刘旻', '北汉主', '联络契丹援军南下'), person('后周转运军民', '粮运与随军人员', '沿泽州道路输送军粮')], image: image('01-new-emperor-marches-to-gaoping', '早春泽州山路,年轻的柴荣穿实用甲胄骑马随禁军北上,步骑与粮车分层行进;对面远山道路上北汉军和契丹骑兵以两支不同队列接近。', 'Early spring on the road into Zezhou. The 32-year-old new emperor Chai Rong rides in practical armor among disciplined Later Zhou infantry, cavalry and spaced grain carts. Across distant foothills, Northern Han troops and a separate Khitan mounted contingent approach in distinct columns. Convey a young regime answering an invasion, not a ceremonial parade, literal map, golden throne or merged ethnic caricature.', '双方在同一纵深接近、车队密度与服甲差异为视觉重构;柴荣亲征及刘旻联契丹南下有史载。'), sources: ['《新五代史·周世宗本纪》卷十二', '《资治通鉴》卷二百九十一'] },
{ id: '02', title: '右翼先溃·御阵重整', timePlace: '高平战场', purpose: '呈现战局由危转胜,不把胜利归结为单人冲锋。', narration: '樊爱能、何徽所部先退,周军右翼动摇。柴荣留在阵前,重新组织未溃部队反击。', overlay: { heading: '有人退,有人留下', line: '高平的转折不在神勇一击,而在溃口没有扩成全军奔逃。' }, people: [person('柴荣', '后周世宗', '留在御阵前整顿军伍'), person('樊爱能', '侍卫马军都指挥使', '临阵先退,战后伏诛'), person('何徽', '侍卫步军都指挥使', '随之退却,战后伏诛')], image: image('02-zhou-line-rallies-after-right-wing-breaks', '高平旷地上,后周右翼一部分骑兵正向后退,柴荣在中军无字大旗下与近卫重新闭合盾矛阵,传令骑手奔向仍在坚持的各队;北汉军从远处压近。', 'On the open Gaoping field, part of the Later Zhou right wing retreats in disorder while Chai Rong remains beneath a blank central standard, helping guards close a shield-and-spear line. Couriers ride toward units that still hold as Northern Han troops press from the distance. Make the reversal collective and plausible; no lone emperor charging thousands, frozen tableau of executions, readable flags or bodies.', '具体阵线、传令与重整动作属视觉推演;樊爱能、何徽先遁而世宗亲督转败为胜有史载。'), sources: ['《新五代史·周世宗本纪》卷十二', '《旧五代史·周书》'] },
{ id: '03', title: '追破高平·战后整军', timePlace: '高平追击线与后周军营', purpose: '交代胜利后严肃军纪、裁汰禁军的长期影响。', narration: '周军追击北汉,再破其军。柴荣战后惩处逃将、裁汰老弱,从高平开始整顿禁军。', overlay: { heading: '胜后先问军法', line: '一场胜仗保住了皇位,战后的整军才真正改变了下一代王朝。' }, people: [person('柴荣', '后周世宗', '检阅军籍并下令整军'), person('樊爱能、何徽', '临阵退却将领', '被按军法处死;不展示行刑'), person('后周禁军', '战后整编部队', '重新点验武器、马匹与人员')], image: image('03-after-gaoping-chai-rong-reforms-army', '战后后周军营,柴荣在无字军籍木案前检阅重新编组的禁军,士卒按弓弩、骑兵和步兵分列,远处两座空置将台暗示逃将被军法处置,不画行刑。', 'After the victory, Chai Rong reviews reorganized Later Zhou units beside blank wooden rolls. Soldiers are sorted into practical infantry, crossbow and cavalry groups while clerks inspect horses and equipment. Two empty command stations in the distance quietly imply the punishment of fleeing generals without depicting execution. Emphasize institutional reform, not triumphal spectacle or a modern drill ground.', '整编场面、军籍木案与空将台为结果性视觉转译;追破刘旻、逃将伏诛及世宗整肃禁军有史载。'), sources: ['《新五代史·周世宗本纪》卷十二', '《资治通鉴》卷二百九十一'] },
],
bgm: { catalogId: 'zhanzhen:hulao#0', title: '虎牢之战', version: '电影级配乐·新君临阵结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删除虎牢专属人物动机和全部人声,只取先受压、重整、反攻的器乐弧线;战后整军迅速降为木鼓与点验声。', reason: '项目暂无高平专曲,虎牢曲的“君主亲临—阵线承压—逆转”结构可复用;仓库暂无成品音频。', cues: [cue('01','00:00','00:25','新君行军,低鼓克制推进。'), cue('02','00:25','00:55','右翼动摇后重整,形成唯一高潮。'), cue('03','00:55','01:23','胜后抽空,以军籍点验收尾。')] },
sources: [{ claim: '世宗亲征、败刘旻于高原与高平、逃将伏诛', citation: '《新五代史·周世宗本纪》卷十二', url: 'https://zh.wikisource.org/zh-hans/新五代史/卷12' }, { claim: '高平战局和战后整军日序', citation: '《资治通鉴》卷二百九十一', url: 'https://zh.wikisource.org/zh-hans/资治通鉴/卷291' }],
blacklist: ['把契丹援军与北汉军画成同一服制的统一军团', '把柴荣画成单骑冲散全军的神将', '把樊爱能、何徽先退简化为后周全军已经彻底战败', '直接展示处决逃将或用军法场面取乐', '现代文字、水印、后世服甲建筑、火器、尸堆与血腥'],
},
{
dir: '北宋/澶渊之役(御驾亲征至盟约)',
event: { canonicalName: '澶渊之役(御驾亲征至盟约)', aliases: ['澶渊之役', '澶州之战', '澶渊之盟'], projectDynasty: '北宋', era: '宋真宗景德元年、辽圣宗统和二十二年', date: '公元1004年九月至十二月', place: '河北诸州、澶州与黄河渡口', summary: '辽承天太后与圣宗率军深入河北,宋真宗在寇准力主下亲赴澶州;辽前锋萧挞凛中宋军伏弩身亡,双方在仍有军力的情况下议和,缔结澶渊之盟。' },
narration: { language: 'zh-CN', estimatedSeconds: 85, fullText: '景德元年秋,辽承天太后萧绰与圣宗耶律隆绪率军南下,越过河北多处州县,兵锋逼近澶州。宋廷有人主张迁都金陵或成都,宰相寇准坚持真宗亲临前线,认为皇帝一旦南退,河北军心就会先散。真宗最终北上,并渡过黄河进入澶州北城。城上将士看见皇帝仪仗,士气有所稳定。与此同时,辽军前锋主帅萧挞凛在阵前察看时,被宋军床子弩射中身亡。主帅阵亡削弱了辽军继续冒险深入的意愿,但这并不是“一箭打赢战争”:辽军仍在城外,宋朝也没有把握全歼对手。双方很快开始谈判。宋使曹利用进入辽营,辽使韩杞往返,最终约为兄弟之国,宋每年输辽银十万两、绢二十万匹。澶渊之盟不是单纯的胜利或屈辱,它以岁币换取边境长期停战,宋辽此后一百多年没有再发生同等规模的全面战争。' },
scenes: [
{ id: '01', title: '辽军深入·朝议南迁', timePlace: '河北道路与开封朝堂', purpose: '建立辽军深入和宋廷路线争论。', narration: '辽军沿河北南下逼近澶州。宋廷出现南迁主张,寇准坚持真宗北上亲征。', overlay: { heading: '是南迁,还是渡河', line: '敌军还没到开封,朝廷先要决定把皇帝放在河北军队的前面还是后面。' }, people: [person('萧绰', '辽承天太后', '与辽圣宗共同领军南下'), person('耶律隆绪', '辽圣宗', '随太后亲征河北'), person('寇准', '北宋宰相', '反对南迁并力主真宗亲征'), person('赵恒', '宋真宗', '在朝议后决定北上')], image: image('01-liao-advances-as-song-court-debates', '初冬河北道路上,萧绰与年轻的辽圣宗在骑兵中查看南进路线,队伍包含契丹骑兵和辎重;画面另一纵深以开封朴素军府中寇准向宋真宗进言,通过驿骑连接而非魔法分屏。', 'Early winter in Hebei. Empress Dowager Xiao Chuo and the young Liao emperor ride among practical Khitan cavalry and baggage, assessing the road south. In a distant architectural depth rather than a magical split screen, a courier reaches a restrained Kaifeng military court where Kou Zhun urges Emperor Zhenzong to go north. Show simultaneous pressure connected by dispatch travel; no nomad caricature, literal arrows or Forbidden City.', '南进队列、同框朝议与驿骑连接为时空压缩式视觉重构;辽帝后亲征、朝议南迁和寇准力主亲征有史载。'), sources: ['《宋史·真宗本纪》卷七', '《辽史·圣宗本纪》卷十四'] },
{ id: '02', title: '真宗登城·挞凛中弩', timePlace: '澶州北城与城外辽阵', purpose: '表现宋军士气稳定和辽主帅阵亡,但拒绝“一箭终战”。', narration: '真宗渡河登上澶州北城。萧挞凛在阵前中床子弩身亡,双方仍保持对峙。', overlay: { heading: '一箭改变谈判', line: '弩箭没有消灭辽军,却让继续深入的代价突然变得更难承受。' }, people: [person('赵恒', '宋真宗', '渡河至澶州北城督战'), person('萧挞凛', '辽南京统军使、前锋主帅', '阵前中伏弩身亡;不展示伤口'), person('澶州守军', '宋军弩手与城防军', '操作床子弩并守住城线')], image: image('02-zhenzong-at-chanzhou-xiao-talin-falls', '澶州夯土城头,宋真宗在克制仪卫旁看向城外,守军操作大型床子弩;远处辽军阵前一匹失去骑手的马被亲兵牵回,暗示萧挞凛中弩,不展示受伤或死亡。', 'On the rammed-earth wall of Chanzhou, Emperor Zhenzong stands with restrained guards while defenders operate a large Song bed-crossbow. Far across the field, attendants lead back a riderless horse from the Liao forward position, implying Xiao Talin’s fatal wound without showing impact or body. Both armies remain intact and alert. Do not make one arrow magically destroy the army or turn the wall into a later Ming fortress.', '真宗站位、床子弩操作和无主坐骑为视觉转译;真宗临澶州、萧挞凛中伏弩死及两军继续对峙有史载。'), sources: ['《宋史·真宗本纪》卷七', '《辽史·圣宗本纪》卷十四'] },
{ id: '03', title: '两营议和·岁币成约', timePlace: '澶州宋辽军营与交割案', purpose: '交代盟约条款和长期结果,不画成单方投降。', narration: '曹利用与韩杞往返两营,宋辽约为兄弟之国,议定每年银十万、绢二十万。', overlay: { heading: '把战线换成账单', line: '银绢按年交割,换来的不是臣服仪式,而是一百多年不再全面开战。' }, people: [person('曹利用', '宋崇仪副使、议和使者', '进入辽营议定岁币'), person('韩杞', '辽飞龙使', '持国书赴宋报聘'), person('宋辽文吏', '盟书与交割经手者', '核对无字木牍、银封和绢帛')], image: image('03-song-liao-negotiate-chanyuan-treaty', '澶州两军营地之间的中立木帐内,曹利用与韩杞隔低案相对,案上是无字木牍、封装银块样品和成卷绢帛;帐外宋辽卫士各守一侧,姿态对等。', 'Inside a neutral timber pavilion between the two camps, Song envoy Cao Liyong and Liao envoy Han Qi face one another across a low table with blank treaty tallies, sealed silver samples and silk rolls. Song and Liao guards remain on separate equal sides outside. Present negotiated parity and accounting, no kneeling surrender, readable document, modern diplomatic table, treasure mountain or celebratory banquet.', '中立帐、样品数量与对等站位为视觉重构;两国遣使、约兄弟之国及岁币数额有史载。'), sources: ['《宋史·真宗本纪》卷七', '《辽史·圣宗本纪》卷十四'] },
],
bgm: { catalogId: 'zhanzhen:chanyuan#0', title: '澶渊之役', version: '电影级配乐·专属器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '辽军南下以低鼓推进,真宗登城与主帅中弩形成短峰,谈判段改用脉冲与绢帛摩擦声;删除人声、歌词和品牌尾签。', reason: '项目已有澶渊专属电影级配乐,战争与议和双重结构完全匹配;仓库暂无成品音频。', cues: [cue('01','00:00','00:28','行军低鼓与朝议低弦交替。'), cue('02','00:28','00:56','登城抬升,中弩后立即收束。'), cue('03','00:56','01:25','谈判改为克制脉冲与绢帛声。')] },
sources: [{ claim: '辽军至澶州、萧挞凛中伏弩及宋辽议和', citation: '《宋史·真宗本纪》卷七', url: 'https://zh.wikisource.org/zh-hans/宋史/卷007' }, { claim: '辽帝后亲征、挞凛阵亡、曹利用请和与韩杞报聘', citation: '《辽史·圣宗本纪》卷十四', url: 'https://zh.wikisource.org/zh-hans/辽史/卷014' }, { claim: '亲征与和议日序', citation: '《资治通鉴长编》相关卷', url: 'https://zh.wikisource.org/zh-hans/续资治通鉴长编' }],
blacklist: ['把萧挞凛中弩画成宋军一箭消灭辽军', '把真宗画成亲自冲锋或寇准独自指挥战场', '把澶渊之盟画成辽向宋或宋向辽跪地投降', '只用“胜利”或“屈辱”遮蔽岁币与长期停战的双重结果', '现代文字、水印、明清城墙服甲、火器、尸堆与血腥'],
},
{
dir: '北宋/靖康之难(汴京陷落与北宋亡)',
event: { canonicalName: '靖康之难(汴京陷落与北宋亡)', aliases: ['靖康之变', '靖康之耻', '金灭北宋之战'], projectDynasty: '北宋', era: '宋钦宗靖康元年至二年、金太宗天会四年至五年', date: '公元1126年至1127年', place: '太原、黄河北岸、东京开封及北行道路', summary: '金军第二次南下包围开封,宋廷在防务、议和与勤王调度中失序;外城陷落后金军控制京城,宋徽宗、钦宗及大量宗室官民被掳北上,北宋灭亡。' },
narration: { language: 'zh-CN', estimatedSeconds: 87, fullText: '宣和末年,宋金联合灭辽后很快反目。靖康元年,金军第一次逼近开封,李纲组织守城,金军议和后退。但宋廷没有重新建立稳定防线,主战、议和与割地命令反复变化,太原等北方据点又相继失守。冬天,金军第二次南下,两路会合包围开封。各地勤王军未能形成统一救援,城内粮食、燃料与军心逐日耗尽。闰十一月,开封外城被攻破。金军并没有立刻把一切变成一场街巷屠杀,而是控制宫城、索取金银、马匹、工匠与人口;宋廷官员被迫在城中搜括。靖康二年,徽宗赵佶、钦宗赵桓和大批宗室、官员、乐工、工匠及百姓被押往北方。北宋朝廷至此结束。靖康之难不是一夜间突然倒塌的城墙,它是燕云政策失败、边防失衡、命令摇摆和围城资源枯竭共同累积的结果。南逃的康王赵构随后建立南宋,宋朝的政治中心也从黄河流域转向江南。' },
scenes: [
{ id: '01', title: '二次南下·汴京再围', timePlace: '黄河至开封外城', purpose: '说明第一次解围后的防线没有恢复,金军两路再次合围。', narration: '第一次围城议和后,太原等据点失守。金军第二次渡河南下,两路会合包围开封。', overlay: { heading: '第二次来的更快', line: '城墙还在,北方的屏障和朝廷对命令的信任已经先失去了。' }, people: [person('完颜宗望、完颜宗翰', '金军东西路统帅', '分别南下并在开封会师'), person('赵桓', '宋钦宗', '在守城、求和与召援之间反复决策'), person('开封守军与居民', '城防军民', '加固城门、运水与储粮')], image: image('01-jin-armies-encircle-kaifeng-again', '北宋开封冬季外城,守军与居民在夯土包砖城墙内搬运木料、水桶和粮袋,城外远处两支金军分别从不同道路建立营垒;规模以烟火与营帐纵深表现。', 'Winter at Northern Song Kaifeng’s outer wall. Defenders and residents move timber, water buckets and dwindling grain inside the period-appropriate earth-and-brick defenses. Outside, two distinct Jin armies establish camps along different approaches, their scale shown by depth and cook fires rather than cloned masses. Show a second siege forming, no instant breach, later Ming battlements, siege cannons or burning city.', '城防劳作、两路营垒与物资状态为视觉重构;金军两路再围开封及城内备守有史载。'), sources: ['《宋史·钦宗本纪》卷二十三', '《金史·太宗本纪》卷三'] },
{ id: '02', title: '外城陷落·搜括议和', timePlace: '开封外城、宫城之间', purpose: '呈现城破后金军控制与宋廷搜括,不用灾难奇观代替制度崩溃。', narration: '外城被攻破后,金军控制京城并要求金银、马匹和人口。宋廷官吏在城内搜括以满足条件。', overlay: { heading: '城破之后先列清单', line: '皇城还没有化为灰烬,朝廷却已把整座城市变成交割物。' }, people: [person('赵桓', '宋钦宗', '接受金军条件并遣官搜括'), person('梅执礼等宋臣', '户部与京城官员', '经手搜括并承担无法完成的责任'), person('开封编户与工匠', '城中居民', '藏匿或交出财物、马匹与工具')], image: image('02-kaifeng-falls-and-city-is-assessed', '开封内城寒冷街巷,金军在路口有序设卡,宋朝官吏面对无字清单核验银器、马具和工匠工具,居民排队但并非跪伏;背景城门受损而非全城燃烧。', 'In a cold Kaifeng street after the outer city falls, Jin troops establish an orderly checkpoint while Song officials work from blank requisition tallies, inspecting silver objects, horse tack and craftsmen’s tools. Residents wait upright with anxious families; a damaged gate stands behind, not a citywide inferno. Focus on coerced administration and extraction, no looting spectacle, sexual violence, readable lists, gore or caricature.', '设卡、交割物与街巷秩序为视觉转译;城陷后金军索取财货人口、宋官搜括有史载。'), sources: ['《宋史·钦宗本纪》卷二十三', '《靖康稗史笺证》所收见证需与正史互证'] },
{ id: '03', title: '二帝北行·北宋终结', timePlace: '开封北门与北上道路', purpose: '交代皇帝宗室及多类人口被掳和南宋建立的转折。', narration: '徽宗、钦宗与宗室官民被押北行。空下的宫城与南去驿骑共同标志北宋灭亡、南宋将起。', overlay: { heading: '一座朝廷被搬走', line: '北行的不只有两位皇帝,还有工匠、乐工、官吏和无数普通家庭。' }, people: [person('赵佶', '宋徽宗、太上皇', '与宗室被押北行'), person('赵桓', '宋钦宗', '被金军带离开封'), person('被掳官民', '官员、工匠、乐工与编户', '在看守下随队北行'), person('赵构', '康王、后为宋高宗', '只以南去驿骑暗示在外另立朝廷')], image: image('03-two-emperors-and-captives-leave-kaifeng', '阴冷清晨的开封北门,徽宗、钦宗穿朴素衣冠坐在受监视车中,宗室、官员、工匠和家庭分组北行;相反方向一名南去驿骑驶离空旷道路,暗示新政权将起。', 'At Kaifeng’s north gate on a bleak morning, the deposed Huizong and Qinzong sit in restrained guarded carts, not cages, while groups of court people, officials, craftspeople and civilian families depart north under watch. On a separate distant road a lone courier rides south, quietly foreshadowing the Southern Song. Keep human scale and dignity; no chain spectacle, naked prisoners, triumphal abuse or corpses.', '人物分组、车驾、南去驿骑为结果性视觉重构;二帝与宗室官民北迁、北宋灭亡及赵构随后即位有史载。'), sources: ['《宋史·钦宗本纪》卷二十三', '《金史·太宗本纪》卷三'] },
],
bgm: { catalogId: 'zhanzhen:shanhaiguanzhizhan#0', title: '山海关之战', version: '电影级配乐·王朝门户崩解结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删除明清专属音色和全部人声,只取门户失守、城市接管、政权北迁的低频结构;搜括段不作动作高潮。', reason: '项目暂无靖康专曲,山海关曲可提供“门户失衡导致政权易手”的器乐弧线;必须彻底移除明清时代指向。仓库暂无成品音频。', cues: [cue('01','00:00','00:28','两路营垒以低频包围。'), cue('02','00:28','00:58','城破不爆炸,改为压迫脉冲。'), cue('03','00:58','01:27','北行抽空,只留车轮与冷弦。')] },
sources: [{ claim: '金军再围、京城失守、二帝北迁与北宋亡', citation: '《宋史·钦宗本纪》卷二十三', url: 'https://zh.wikisource.org/zh-hans/宋史/卷023' }, { claim: '金太宗时期灭宋行动的金方帝纪', citation: '《金史·太宗本纪》卷三', url: 'https://zh.wikisource.org/zh-hans/金史/卷003' }, { claim: '靖康围城编年与宋廷处置', citation: '《续资治通鉴》相关卷', url: 'https://zh.wikisource.org/zh-hans/续资治通鉴' }],
blacklist: ['把两次围开封压成同一天,忽略第一次议和与第二次合围', '把靖康之难只画成宫廷羞辱,遮蔽城防、搜括与人口迁徙', '用全城火海、裸体俘虏或性暴力制造灾难奇观', '把北宋灭亡归于单一奸臣或单一城门失守', '现代文字、水印、明清城防服甲、火器、尸堆与血腥'],
},
{
dir: '辽/雍熙北伐(岐沟关败至杨业被俘)',
event: { canonicalName: '雍熙北伐(岐沟关败至杨业被俘)', aliases: ['雍熙北伐', '岐沟关之战', '陈家谷之战', '杨业被俘'], projectDynasty: '辽', era: '辽圣宗统和四年、宋太宗雍熙三年', date: '公元986年三月至七月', place: '幽州、岐沟关、拒马河、朔州、陈家谷一线', summary: '宋太宗分三路北伐燕云,初期攻取山后州县;东路曹彬在岐沟关败退后全局逆转,辽军收复失地,宋将杨业掩护军民南撤时在陈家谷一带孤军作战,被辽军俘获后绝食而死。' },
narration: { language: 'zh-CN', estimatedSeconds: 88, fullText: '雍熙三年,宋太宗发动三路北伐,目标是夺取燕云地区。东路曹彬进向幽州,中路田重进出飞狐,西路潘美、杨业攻云州、应州、朔州。战事初期,多座山后城池归附宋军,北伐看上去进展迅速。辽朝由承天太后萧绰临朝,十五岁的圣宗在位,耶律休哥、耶律斜轸分别组织反击。曹彬东路粮运困难,又在进退之间失去秩序,最终于岐沟关一带大败,渡拒马河撤退。东路溃败使三路无法互相支撑,西路奉命转为护送云、应等州居民南撤。杨业认为辽军锋锐,应避开正面决战,但监军和诸将催促出战。他率军迎敌,并约定在陈家谷口接应;等他力战退回时,谷口援军已经离开。杨业重伤被俘,绝食而死。辽军随后复朔州等失地。雍熙北伐不是杨家将传奇中的孤胆故事,而是一场三路协同和补给体系先行崩解、最后由一支掩护部队承担代价的战争。' },
scenes: [
{ id: '01', title: '三路北进·山后易帜', timePlace: '燕云南缘与云应诸州', purpose: '建立北伐初期进展和三路相隔的结构性风险。', narration: '宋军分东中西三路推进,山后多城归附。三路战果迅速,却被遥远道路和不同粮道分隔。', overlay: { heading: '三路都在前进', line: '捷报来自三个方向,彼此之间却没有一支军队能够随时救援另一支。' }, people: [person('曹彬', '宋军东路主帅', '率主力向幽州推进'), person('田重进', '宋军中路主帅', '从飞狐方向北进'), person('潘美、杨业', '宋军西路将领', '攻取云、应、朔等州')], image: image('01-three-song-columns-enter-yanyun', '北方丘陵与宽阔道路的远景中,三支宋军分别在不同纵深向北行进,各自带粮车、骡马和无字旗,山后小城官吏在城门交出钥匙;三路彼此不相连。', 'Across broad northern hills, three Song columns move north on widely separated roads at different depths, each with its own carts, pack animals and blank standards. At one modest hill city, local officials surrender a gate key. Represent early gains and dangerous separation without a literal map, glowing route lines, one synchronized super-army or modern aerial view.', '三路同框、城门交接与道路间距为时间压缩式视觉重构;宋军三路北伐及山后州县初附有史载。'), sources: ['《宋史·太宗本纪》卷五', '《辽史·圣宗本纪》卷十一'] },
{ id: '02', title: '岐沟关败·东路先溃', timePlace: '岐沟关至拒马河', purpose: '说明曹彬东路败退如何使全局失去协同。', narration: '曹彬军粮运紧张,在岐沟关败于辽军,夜渡拒马河撤退。北伐主力先失去立足点。', overlay: { heading: '最重的一路先退', line: '东路一旦过河,西路攻下的城池就从战果变成了必须带走的人口。' }, people: [person('曹彬', '宋军东路主帅', '在岐沟关败后收军南撤'), person('耶律休哥', '辽军统帅', '袭扰宋军粮道并组织追击'), person('宋军役夫与士卒', '东路转运和作战人员', '在夜色中渡拒马河')], image: image('02-song-east-army-retreats-across-juma-river', '黄昏拒马河浅滩,宋军东路步卒、马匹和空粮车分批过河,曹彬在岸边收束队列;远方辽骑保持压力但未冲入水中,遗弃粮袋说明补给恶化。', 'At dusk on a Juma River ford, Song infantry, horses and nearly empty carts cross south in separated groups while Cao Bin tries to maintain a rear guard. Distant Liao cavalry applies pressure without a theatrical charge into the water; abandoned grain sacks show the logistical failure. No giant flood, drowning men, total massacre or later stone bridge.', '渡河秩序、空粮车与追军距离为视觉重构;曹彬岐沟关败、夜渡拒马河退军有史载。'), sources: ['《宋史·太宗本纪》卷五', '《资治通鉴长编》雍熙三年条'] },
{ id: '03', title: '陈家谷空·杨业被俘', timePlace: '朔州南、陈家谷口', purpose: '呈现掩护撤民、援军离位和被俘结局,剥离演义虚构。', narration: '杨业掩护云应军民南撤,力战退至陈家谷时没有等到接应,重伤被俘,辽军复取朔州。', overlay: { heading: '谷口没有援军', line: '传奇记住了一个人的死,战争真正暴露的是一整条指挥链的断裂。' }, people: [person('杨业', '宋云州观察使、副将', '掩护军民撤退,力战被俘'), person('耶律斜轸', '辽北院枢密使、反击主帅', '收复朔州并俘获杨业'), person('云应州军民', '内迁居民与护送者', '携车马向南撤离')], image: image('03-yang-ye-returns-to-empty-chenjia-valley', '暮色陈家谷口,负伤但未见血的杨业与少数骑兵退到约定接应处,谷中只有空营火和车辙;辽军从远处山脊合围,另一侧云应州居民车队已向南离开。', 'At twilight at the mouth of Chenjia Valley, an exhausted and wounded but bloodless Yang Ye returns with only a few mounted soldiers to the promised relief point. The valley holds extinguished campfires and empty wheel tracks; Liao forces appear on distant ridges while civilian carts from Yun and Ying continue south in another depth. No legendary suicide at a stone monument, magical ambush, gore or opera costume.', '空营火、车辙、居民车队与合围山脊为视觉转译;杨业约谷口接应、援军先退、力战被俘及辽复朔州有史载。'), sources: ['《辽史·圣宗本纪》卷十一', '《宋史·杨业传》卷二百七十二'] },
],
bgm: { catalogId: 'zhanzhen:baideng#0', title: '白登之围', version: '电影级配乐·北地合围结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '删除汉匈专属动机和全部人声,只取北地三路推进、主路溃退与谷口合围的器乐结构;杨业被俘处不做英雄爆燃。', reason: '项目暂无雍熙北伐专曲,白登曲的北方远征与被围结构适合承载本包,但必须避免混淆汉代背景。仓库暂无成品音频。', cues: [cue('01','00:00','00:29','三路推进以分离节拍进入。'), cue('02','00:29','00:59','东路退河,节拍逐层失序。'), cue('03','00:59','01:28','谷口抽空,以远处低号收束。')] },
sources: [{ claim: '宋军三路北伐与东路败退', citation: '《宋史·太宗本纪》卷五', url: 'https://zh.wikisource.org/zh-hans/宋史/卷005' }, { claim: '辽军复朔州、俘杨继业', citation: '《辽史·圣宗本纪》卷十一', url: 'https://zh.wikisource.org/zh-hans/辽史/卷011' }, { claim: '杨业出战、谷口失援、被俘绝食', citation: '《宋史·杨业传》卷二百七十二', url: 'https://zh.wikisource.org/zh-hans/宋史/卷272' }],
blacklist: ['把三路北伐画成三军始终并肩推进', '把杨业写成无条件主动求战,忽略其避锋建议与被迫出战', '把陈家谷画成演义中的撞碑自尽或七郎八虎传奇', '只归因于某一监军个人,遮蔽东路溃败与全局协同失败', '现代文字、水印、明清服甲建筑、火器、尸堆与血腥'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((item) => `${item.name}(${item.office};${item.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((item) => `| ${item.from}–${item.to} | ${item.scene} | ${item.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory)) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Five Dynasties, Northern Song and Liao core battle packages`)
@@ -0,0 +1,233 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outputRoot = path.join(repoRoot, '战役素材', '西汉')
const VISUAL_BASE = `Style/medium: premium live-action Chinese historical film still, archaeology-inspired Chu-Han realism around 205–204 BCE, subtle film grain and physically plausible scale. Materials: rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered lamellar armor, wooden carts, bronze and iron weapons; armies must have varied worn clothing rather than uniform costume-pageant outfits. Composition: cinematic 16:9 landscape with distinct foreground, middle ground and background; reserve quiet sky, wall, water or earth for webpage overlay. Constraints: no baked-in text, readable banners, names, subtitles, logo or watermark; violence through movement, broken equipment and consequences, never gore. Avoid: clone-face armies, fantasy armor, later Han/Tang/Song/Ming/Qing armor or architecture, imperial yellow dragon robes, bead-curtain crowns, Forbidden City, modern maps, UI arrows, firearms, cannons, explosions, samurai armor, European plate armor, magic, anime or body piles.`
function image(file, alt, request, visualInference) {
return { original: `images/${file}.png`, delivery: `images/${file}.webp`, alt, prompt: `Use case: historical-scene. Asset type: Digital Me Chu-Han battle story frame, 16:9 landscape. Primary request: ${request} ${VISUAL_BASE}`, visualInference, refinementPrompt: '' }
}
const packs = [
{
dir: '彭城之战(项羽三万破汉军)',
event: {
canonicalName: '彭城之战(项羽三万破汉军)', aliases: ['彭城之战', '睢水之战', '彭城大败'], era: '楚汉战争', date: '公元前205年四月',
place: '彭城及谷水、泗水、灵壁东睢水一线(今江苏徐州至安徽灵璧一带)',
summary: '刘邦率五诸侯联军入彭城后松懈,项羽自齐地率精兵三万回击,晨战至日中即大破汉军,并追至睢水。'
},
narration: {
language: 'zh-CN', estimatedSeconds: 88,
fullText: '公元前二〇五年,项羽北上攻齐,刘邦趁彭城空虚,统合五路诸侯军东进。《史记》记这支联军共有五十六万。汉军进入彭城后收取财货,日日置酒高会,胜利仿佛已经结束。项羽没有带回全部楚军,而是留下诸将继续攻齐,亲率精兵三万,由鲁地南下,经胡陵快速回援。清晨,楚军从萧县方向突击汉军,战到日中,庞大的联军已经全面崩溃。败兵先拥入谷水、泗水,楚军又追至灵壁以东的睢水。河流截住退路,史书用“睢水为之不流”形容伤亡之重。刘邦被楚军数重包围,忽有西北大风扬沙折木,楚军阵势一时混乱,他才带数十骑逃走。彭城让项羽以三万精兵夺回都城,也让刘邦失去父母妻子和大批诸侯盟军;但他仍退往荥阳,楚汉战争并未结束。'
},
scenes: [
{
id: '01', title: '入城高会', timePlace: '公元前205年四月,彭城', purpose: '表现汉军占领彭城后的松懈,为项羽突袭建立前因。',
narration: '刘邦率五诸侯联军进入彭城。军中收取财货,日日置酒高会,防备逐渐松弛。',
overlay: { heading: '入城高会', line: '五十六万人的胜势,在都城里先变成了松懈。' },
people: [
{ name: '刘邦', office: '汉王', action: '统合五诸侯军进入彭城' },
{ name: '五诸侯联军', office: '汉方联军', action: '占领彭城;史载合计五十六万,具体五诸侯身份存争' }
],
image: image('01-feast-in-pengcheng', '彭城内的楚王宫外院,刘邦与数名诸侯将领在简朴长案旁饮宴,院外汉军搬运箱笼、卸下甲胄休息,城防显得松散但不铺陈奢靡后宫。', 'Courtyard inside captured Pengcheng. Liu Bang, a pragmatic middle-aged East Asian ruler with weathered face, thin moustache and short beard, wears a dark Han king robe over light practical armor and sits at a low wooden table with several allied lords. Cups, food vessels and unopened chests suggest a victory feast; outside the gate mixed allied soldiers unload goods, rest without helmets and leave watch posts thin. Show complacency and dispersed attention, not a decadent fantasy palace or sexualized captives.', '宴席位置、财货箱笼、卸甲与守备松散为视觉重构;入城后收货宝、日置酒高会有史载。'),
sources: ['《史记·项羽本纪》卷七', '《史记·高祖本纪》卷八']
},
{
id: '02', title: '三万回师', timePlace: '齐地至鲁、胡陵方向', purpose: '突出项羽舍大军、以精兵和速度回援彭城。',
narration: '项羽留下诸将继续攻齐,自己只带精兵三万,从鲁地经胡陵昼夜南下。',
overlay: { heading: '三万回师', line: '他没有比人数,而是先和时间赛跑。' },
people: [
{ name: '项羽', office: '西楚霸王', action: '自率精兵三万回援彭城' },
{ name: '楚军精兵', office: '快速回师部队', action: '由齐地南经鲁、胡陵方向进军' }
],
image: image('02-thirty-thousand-return', '黎明前的鲁地道路上,项羽骑在队伍前方,楚军精兵以紧凑长列快速南下,骑兵、步卒和少量粮车比例可信,远处仍是夜色。', 'Pre-dawn road through the Lu region. Xiang Yu, a powerful young East Asian commander in his mid-twenties with broad shoulders, tied hair, weathered face and practical dark Chu lamellar armor with muted red-brown cloth, rides at the leading edge of a compact fast-moving column. Selected infantry and cavalry move south in disciplined files with only light baggage and a few carts; tired torchlight fades into cold dawn. Convey speed, selection and urgency, not a gigantic parade or impossible cavalry-only army.', '具体道路、昼夜节奏、兵种与轻装辎重为视觉补全;项羽自将精兵三万南从鲁出胡陵有史载。'),
sources: ['《史记·项羽本纪》卷七']
},
{
id: '03', title: '晨击日破', timePlace: '萧县至彭城一线,清晨至日中', purpose: '表现楚军快速冲击与联军指挥崩解,不把“以少胜多”变成超人单骑。',
narration: '项羽从萧县方向清晨发起攻击。战到日中,联军各部互相挤压,彭城防线已经崩溃。',
overlay: { heading: '晨击日破', line: '半日之间,人数优势变成了互相阻塞的退路。' },
people: [
{ name: '项羽', office: '西楚霸王、楚军主帅', action: '亲自指挥晨击并夺回彭城' },
{ name: '刘邦', office: '汉王、联军主帅', action: '在突袭中组织失效并转入撤退' },
{ name: '汉诸侯联军', office: '临时联盟军队', action: '遭快速突击后瓦解' }
],
image: image('03-dawn-strike-noon-rout', '彭城西侧平原上,项羽在楚军冲击中指挥推进,汉诸侯联军不同装束的部队在狭窄道路和营门间互相阻塞,破损车阵显示崩溃但不见尸山。', 'Morning assault outside Pengcheng. Xiang Yu, same identity as scene 02, directs compact Chu shock groups through the gaps of a sprawling allied camp. Han contingents in visibly different regional clothing attempt to turn, form ranks and retreat through the same narrow camp entrances, creating command breakdown rather than cowardly caricature. Liu Bang appears farther back on horseback trying to withdraw with guards. Broken wagons and abandoned drums suggest collapse; wide diagonal action, no superhero leap, no body piles.', '营门、道路阻塞、各部装束与两位主帅的同框为视觉推演;晨击至日中大破汉军见史载。'),
sources: ['《史记·项羽本纪》卷七', '《史记·高祖本纪》卷八']
},
{
id: '04', title: '退至睢水', timePlace: '灵壁东睢水沿岸', purpose: '呈现地形把溃退扩大为灾难,对惨烈数字保持克制。',
narration: '败军越过谷水、泗水,仍被追到睢水。河岸拥塞,退路在水边彻底断裂。',
overlay: { heading: '退至睢水', line: '战败先发生在军阵里,灾难随后发生在河岸。' },
people: [
{ name: '汉军败卒', office: '汉与诸侯联军士卒', action: '被追至睢水,争渡中伤亡惨重' },
{ name: '楚军追兵', office: '项羽所部', action: '沿谷水、泗水追击至睢水' }
],
image: image('04-rout-at-sui-river', '睢水岸边,汉军败卒被堵在浅滩和毁坏车辆之间,有人涉水、有人抛下盾牌,楚军追兵在远处止步列阵,水面没有漂尸特写。', 'Wide riverbank at the Sui River during the rout. Disordered Han and allied soldiers crowd around broken carts and narrow shallows, some wading chest-deep, others dropping shields to seek another crossing. Chu pursuit formations remain in the middle distance pressing forward, not committing graphic slaughter in the foreground. Use churned water, abandoned equipment and panic to convey the historical catastrophe; no visible corpses, no red river, no drowning close-ups. Flat gray daylight and broad water provide restrained negative space.', '渡点、浅滩深度、败军器械与追兵距离为视觉补全;追至灵壁东睢水、汉卒大批入水有史载。'),
sources: ['《史记·项羽本纪》卷七', '《史记·高祖本纪》卷八']
},
{
id: '05', title: '大风脱围', timePlace: '睢水战场外缘,溃败后', purpose: '表现史载西北大风造成楚军短暂混乱、刘邦数十骑逃脱,并交代战略后果。',
narration: '刘邦被数重包围时,西北大风扬沙折木,楚军一时散乱。他带数十骑逃出,父母妻子却落入楚军。',
overlay: { heading: '大风脱围', line: '项羽赢下彭城,刘邦却还活着离开了。' },
people: [
{ name: '刘邦', office: '汉王', action: '借楚军受风沙扰乱之机率数十骑逃脱' },
{ name: '楚军', office: '追击军队', action: '突遇西北大风后阵势短暂混乱' },
{ name: '吕雉与刘太公', office: '汉王妻与父', action: '在彭城败后被楚军俘获;不虚构本幕相见' }
],
image: image('05-wind-opens-an-escape', '西北大风卷起黄尘和折枝,刘邦与数十骑从混乱的楚军包围边缘疾驰而出,远处楚军遮目稳马,画面不把自然风表现成神术。', 'A violent natural northwest wind sweeps dust, loose thatch and broken branches across the edge of the battlefield. Liu Bang, consistent with scene 01, escapes with a visibly small group of several dozen riders through a temporary opening; farther back Chu soldiers shield their eyes and control startled horses as formations blur. No magical aura, no lightning, no divine intervention. Wide lateral movement and low visibility, the escape narrow and exhausted rather than triumphant.', '骑行路线、沙尘密度、具体包围缺口与折枝为视觉重构;西北大风使楚军大乱、汉王以数十骑遁有史载。'),
sources: ['《史记·项羽本纪》卷七']
}
],
bgm: {
catalogId: 'zhanzhen:pengcheng#0', title: '彭城之战', version: '电影级配乐·建议使用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '第一幕压低节奏表现虚浮安逸,第二幕建立快速行军脉冲,第三幕形成唯一爆发,第四幕撤去英雄动机,仅留低频与水声,最后用风沙和残余低弦收束;删除歌词与品牌尾签。',
reason: '项目内与彭城直接绑定的自生成音乐,项羽三万回师与半日击溃的上扬结构契合五幕推进;仓库内暂无成品音频。',
cues: [
{ from: '00:00', to: '00:18', scene: '01', direction: '宴饮器物声下压,保持不祥静态。' },
{ from: '00:18', to: '00:36', scene: '02', direction: '加入快速但克制的行军脉冲。' },
{ from: '00:36', to: '00:54', scene: '03', direction: '全曲唯一爆发,随后立刻失稳。' },
{ from: '00:54', to: '01:12', scene: '04', direction: '撤去英雄旋律,以低频、水声和碎裂节奏表现溃退。' },
{ from: '01:12', to: '01:30', scene: '05', direction: '风声覆盖鼓点,留下未结束的悬念。' }
]
},
sources: [
{ claim: '五诸侯兵力、入彭城高会、项羽三万回师、晨击日破、睢水追击与大风脱围', citation: '《史记·项羽本纪》卷七', url: 'https://zh.wikisource.org/zh-hans/史记/卷007' },
{ claim: '刘邦入彭城、睢水大败、家属被俘及诸侯复归楚', citation: '《史记·高祖本纪》卷八', url: 'https://zh.wikisource.org/zh-hans/史记/卷008' }
],
blacklist: [
'把汉军五十六万与楚军三万理解为可逐人核验的现代统计;按《史记》记数并注明联军性质',
'把项羽三万全部画成重骑兵;原文只称“精兵”,不明言单一兵种',
'把睢水惨败画成血河、尸山或近景溺亡奇观',
'把西北大风写成项羽或刘邦召来的神迹,或夸大为龙卷风和雷电法术',
'虚构五诸侯确定名单、楚汉将领单挑或一支军旗决定胜负',
'现代文字、字幕、地图箭头、Logo、水印、后世盔甲建筑、火器爆炸或动漫画风'
]
},
{
dir: '井陉之战(背水一战)',
event: {
canonicalName: '井陉之战(背水一战)', aliases: ['井陉口之战', '背水一战', '韩信破赵'], era: '楚汉战争', date: '公元前204年',
place: '井陉口、赵军营壁及泜水一带(今河北井陉附近;部分水系地望存争)',
summary: '韩信、张耳东下井陉,以万人背水列阵、二千轻骑乘赵军空营拔帜易帜,夹击破赵,斩陈馀并擒赵王歇。'
},
narration: {
language: 'zh-CN', estimatedSeconds: 89,
fullText: '公元前二〇四年,韩信与张耳率军东下井陉攻赵。赵王歇、成安君陈馀在井陉口聚兵,号称二十万。广武君李左车建议拨三万奇兵绕道切断汉军辎重,再深沟高垒拒战;陈馀自称义兵不用诈谋,没有采纳。韩信派人侦察,确认赵军不用此计,才敢继续入陉。他先选二千轻骑,每人持一面赤帜,从小路隐蔽在山后;又让一万人先出井陉口,背水列阵。天明,韩信打出主将旗鼓诱赵军出营争夺。双方久战,汉军佯弃旗鼓退向水边,背水士卒无路可退,人人死战。赵军倾巢追击时,二千轻骑驰入空营,拔去赵旗,换上汉军赤帜。赵军回望营垒,误以为主将已被擒,阵势顿乱。汉军前后夹击,大破赵军,斩陈馀、擒赵王歇。背水并非孤注一掷,而是与侦察、诱敌和拔帜奇兵同时咬合的一整套部署。'
},
scenes: [
{
id: '01', title: '上策不用', timePlace: '井陉口赵军营帐', purpose: '交代李左车断粮建议与陈馀拒绝,为韩信敢于入陉建立情报前提。',
narration: '李左车建议以三万奇兵切断汉军辎重,再坚营不战。陈馀拒绝了这套诈谋。',
overlay: { heading: '上策不用', line: '井陉最危险的那条路,赵军主动让了出来。' },
people: [
{ name: '李左车', office: '赵广武君', action: '献奇兵绝辎重、深沟高垒之策' },
{ name: '陈馀', office: '赵成安君、前线主帅', action: '以义兵不用诈谋为由拒绝' },
{ name: '赵王歇', office: '赵王', action: '名义统帅,在军中' }
],
image: image('01-li-zuoche-plan-rejected', '赵军营帐中,李左车用无字木筹和几只石块指示狭道与粮车,陈馀端坐听取却抬手拒绝,赵王歇在侧,不出现现代沙盘地图。', 'Inside the Zhao command tent near Jingxing Pass. Li Zuoche, an older East Asian strategist in plain layered robes, uses blank wooden counters, cord and a few stones on a rough table to explain a narrow route and baggage column without a drawn map. Chen Yu, a dignified middle-aged Confucian commander in restrained Zhao armor and robe, raises one hand to reject the proposal; Zhao King Xie listens from the side. The disagreement is controlled and intellectual, not shouting. Open flap reveals steep valley walls and fortified camp.', '木筹示意、人物神态、营帐与山谷同框为视觉补全;李左车献断粮策、陈馀不用有史载。'),
sources: ['《史记·淮阴侯列传》卷九十二']
},
{
id: '02', title: '背水列阵', timePlace: '井陉口外临水地带,夜末', purpose: '呈现万人先行背水布阵,同时说明这只是整体部署的一部分。',
narration: '韩信让一万人先出井陉口,背水列阵。赵军从营垒远望,见此布置大笑。',
overlay: { heading: '背水列阵', line: '后面是水,但真正的退路被放在了敌营里。' },
people: [
{ name: '韩信', office: '汉左丞相、前线主帅', action: '命万人背水列阵' },
{ name: '张耳', office: '汉将', action: '与韩信共同东击赵' },
{ name: '汉军步卒', office: '背水本阵', action: '临水列阵并准备承受赵军主攻' }
],
image: image('02-formation-against-water', '黎明前河岸上,韩信在侧后方检查万人背水阵,步卒以盾矛列出层次,后方水面紧贴阵线,远处赵军营壁在山口高处。', 'Before dawn beside an unnamed river near Jingxing. Han Xin, a lean East Asian commander in his early thirties with composed face, tied hair, neat moustache, dark practical Han lamellar armor and muted red cloak, inspects a broad infantry formation whose backs are visibly close to the water. Zhang Er stands with aides farther along the bank. Shield and spear ranks are tense and imperfect, not identical clones; across the valley Zhao campfires and ramparts look down from higher ground. Strategic wide shot, cold blue-gray morning.', '具体水岸、阵列宽度、韩信与张耳站位为视觉补全;万人先行、出背水陈及赵军望见大笑有史载。'),
sources: ['《史记·淮阴侯列传》卷九十二']
},
{
id: '03', title: '弃旗诱出', timePlace: '平旦,井陉口与赵营之间', purpose: '表现韩信以主将旗鼓诱赵军空营逐利,并与背水本阵衔接。',
narration: '天明,韩信打出主将旗鼓出井陉口。久战后汉军佯弃旗鼓退向水边,赵军空营追击。',
overlay: { heading: '弃旗诱出', line: '被丢下的旗鼓,不是败象,而是让赵营变空的诱饵。' },
people: [
{ name: '韩信', office: '汉左丞相', action: '建大将旗鼓诱赵军出壁,并佯退水上军' },
{ name: '陈馀', office: '赵军主帅', action: '开壁出击并争夺汉军旗鼓' },
{ name: '汉军背水阵', office: '本阵步卒', action: '接纳退回部队并殊死抵抗' }
],
image: image('03-banners-abandoned-as-bait', '井陉口外战场上,汉军把主将旗鼓留在破损车阵旁向河岸撤回,赵军从打开的营门争相追出,韩信在退却队列中回望控制节奏。', 'Battlefield between Jingxing Pass and the Zhao camp at first light. Han troops deliberately abandon large command drums and plain, unreadable standard poles beside a broken wagon line while falling back toward the riverside formation. Zhao forces stream from their opened fortified gate to seize the apparent prizes, leaving the camp visibly thin. Han Xin, same identity as scene 02, rides within the controlled withdrawal and looks back, directing rather than panicking. Dynamic deep composition; standards carry solid muted color only, no symbols or writing.', '弃置位置、赵军出营规模、韩信回望与车阵为视觉重构;建大将旗鼓、佯弃鼓旗、赵空壁争夺有史载。'),
sources: ['《史记·淮阴侯列传》卷九十二']
},
{
id: '04', title: '拔帜易帜', timePlace: '赵军空营与萆山间道', purpose: '呈现二千轻骑乘虚入营换旗这一攻心枢纽。',
narration: '二千轻骑从山间小路驰入空营,拔去赵旗,遍立汉军赤帜。赵军回望时,看到营垒已经变色。',
overlay: { heading: '拔帜易帜', line: '真正击垮赵军的,是回头那一眼。' },
people: [
{ name: '汉军轻骑', office: '二千奇兵', action: '由间道隐蔽,乘赵军空营拔帜立赤帜' },
{ name: '韩信', office: '汉军主帅', action: '预先部署二千轻骑执行攻心奇袭;本人不在营内' }
],
image: image('04-cavalry-switches-standards', '赵军空营内,汉军轻骑翻越低矮营壁,拔下素色赵旗并竖起无字赤帜,营外山道仍有骑兵进入,远方战场上的赵军开始回望。', 'Inside the nearly empty Zhao fortified camp. Han light cavalry dismount and climb low earth-and-timber ramparts, pulling down plain Zhao-colored standards and raising many solid red Han standards with absolutely no writing or symbols. More riders enter through a rear mountain path; abandoned tents and tether rails show the garrison rushed out. Far across the valley, small Zhao formations begin turning back toward the transformed camp. Emphasize coordinated infiltration and psychological shock, not a flag-waving celebration.', '营壁高度、旗色分布、入营路径与远方回望为视觉重构;二千轻骑从间道入空壁、拔赵旗立汉赤帜有史载。'),
sources: ['《史记·淮阴侯列传》卷九十二']
},
{
id: '05', title: '夹击破赵', timePlace: '赵营与泜水一线,战斗终局', purpose: '交代赵军心理崩溃、汉军夹击和战果,并保留李左车被礼待的余韵。',
narration: '赵军见满营赤帜,以为主将已失,顿时溃乱。汉军前后夹击,斩陈馀、擒赵王歇。',
overlay: { heading: '夹击破赵', line: '背水阵守住正面,营中的赤帜从背后击垮军心。' },
people: [
{ name: '韩信', office: '汉军主帅', action: '完成前后夹击并战后师事李左车' },
{ name: '陈馀', office: '赵成安君', action: '兵败,后在泜水被斩;不作血腥近景' },
{ name: '赵王歇', office: '赵王', action: '战败被擒' },
{ name: '李左车', office: '赵广武君', action: '被俘后获韩信解缚礼待' }
],
image: image('05-zhao-defeated-li-honored', '战后赵营外,赵王歇站立交出佩剑,韩信命士卒为李左车解开绳索并让座,远处汉军赤帜遍布营壁,陈馀之死不在画面中。', 'Restrained aftermath outside the captured Zhao camp. Zhao King Xie stands upright surrendering his sheathed sword to guards, neither bound nor humiliated. Han Xin, consistent face and armor, gestures for soldiers to remove a simple rope from the wrists of the older strategist Li Zuoche and offers him a place beside a low camp table, foreshadowing respectful consultation. Zhang Er observes. Solid red standards line the ramparts in the distance without marks. Chen Yu’s death is not shown. Quiet exhaustion and strategic respect, no mass kneeling or triumphal spectacle.', '赵王交剑、李左车解缚让座与人物同框为视觉重构;擒赵王、勿杀李左车并解缚师事有史载,陈馀之死只用文案交代。'),
sources: ['《史记·淮阴侯列传》卷九十二', '《史记·张耳陈余列传》卷八十九']
}
],
bgm: {
catalogId: 'zhanzhen:jingxing#0', title: '井陉之战', version: '电影级配乐·建议使用无主唱器乐 stem', status: 'pending-export', pathOrUrl: '',
usage: '保留算盘般的冷钢琴动机、绷紧低弦与一次深鼓;第一幕极冷,第二幕压住不爆,第三幕收窄,第四幕在赤帜立起时形成唯一精准爆点,第五幕重新冻结;删除歌词与品牌尾签。',
reason: '项目内与井陉直接绑定的自生成音乐,其冷静算计和“赤帜一刻”单爆结构与五幕战术链完全对应;仓库内暂无成品音频。',
cues: [
{ from: '00:00', to: '00:18', scene: '01', direction: '以冷钢琴和单音低弦表现被拒绝的上策。' },
{ from: '00:18', to: '00:36', scene: '02', direction: '加入持续低音,不做热血背水主题。' },
{ from: '00:36', to: '00:54', scene: '03', direction: '抽掉鼓点,拉紧诱敌过程。' },
{ from: '00:54', to: '01:12', scene: '04', direction: '赤帜立起时出现唯一短促爆点。' },
{ from: '01:12', to: '01:30', scene: '05', direction: '迅速回到冷钢琴,以战后礼待收束。' }
]
},
sources: [
{ claim: '李左车献策、陈馀不用、万人背水、二千骑拔帜、夹击战果与韩信战后论兵', citation: '《史记·淮阴侯列传》卷九十二', url: 'https://zh.wikisource.org/zh-hans/史记/卷092' },
{ claim: '张耳陈馀交恶背景及井陉战后陈馀、赵王结局互证', citation: '《史记·张耳陈余列传》卷八十九', url: 'https://zh.wikisource.org/zh-hans/史记/卷089' }
],
blacklist: [
'把赵军“号称二十万”当作已核实实数;汉军总数也不可简单写成三万或数千的确定值',
'只画背水列阵而忽略侦察、诱敌空营与二千轻骑拔帜的完整战术链',
'把陈馀描写成不会打仗的丑角;他拒绝的是李左车的诈谋奇计,史书保留其决策理由',
'在赤帜上生成可读“汉”字、现代军徽或统一印刷图案',
'断言背水所临水体与泜水的现代精确位置,或制作无争议的现代路线图',
'现代字幕、Logo、水印、后世盔甲建筑、火器爆炸、奇幻法术、动漫画风或血腥斩首'
]
}
]
function markdown(pack) {
const out = [
`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '',
`- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '',
'## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', ''
]
for (const scene of pack.scenes) out.push(
`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`,
`- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`,
`- 人物:${scene.people.map((p) => `${p.name}(${p.office};${p.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', ''
)
out.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, `- 音频路径或 URL:${pack.bgm.pathOrUrl || '仓库暂缺,待导出'}`, '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((c) => `| ${c.from}–${c.to} | ${c.scene} | ${c.direction} |`), '', '## 史料来源', '', ...pack.sources.map((s) => `- ${s.claim}:[${s.citation}](${s.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((x) => `- ${x}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', `- [${pack.bgm.status === 'ready' ? 'x' : ' '}] BGM 已导出并填写可用路径`, '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${out.join('\n')}\n`
}
for (const pack of packs) {
const dir = path.join(outputRoot, pack.dir)
if (fs.existsSync(dir)) throw new Error(`Refusing to overwrite existing package: ${dir}`)
fs.mkdirSync(path.join(dir, 'images'), { recursive: true })
const manifest = { event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }
fs.writeFileSync(path.join(dir, 'manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`)
fs.writeFileSync(path.join(dir, '素材说明.md'), markdown(pack))
}
console.log(`Generated ${packs.length} Chu-Han battle packages under ${outputRoot}`)
@@ -0,0 +1,58 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材', '西汉')
const BASE = `Premium live-action Chinese historical film still, archaeology-inspired Western Han realism, cinematic 16:9 landscape, subtle film grain, physically plausible human and terrain scale, tactile rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered lamellar armor, wood carts, bronze and iron tools. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, modern maps, UI arrows, firearms, cannons, explosions, samurai armor, European plate, magic, anime, gore or body piles.`
const img = (file, alt, request, inference) => ({ original:`images/${file}.png`, delivery:`images/${file}.webp`, alt, prompt:`Use case: historical-scene. Digital Me Western Han story frame. ${request} ${BASE}`, visualInference:inference, refinementPrompt:'' })
const packs = [
{
dir:'七国之乱(周亚夫断吴楚粮道)',
event:{ canonicalName:'七国之乱(周亚夫断吴楚粮道)', aliases:['七国之乱','吴楚七国之乱','吴楚之乱'], era:'汉景帝三年', date:'公元前154年正月至三月', place:'关东诸侯国、梁国与昌邑一线', summary:'吴楚等七国以诛晁错为名起兵;景帝虽杀晁错仍未止兵,周亚夫坚壁断吴楚粮道,朝廷三月平乱。' },
narration:{ language:'zh-CN', estimatedSeconds:84, fullText:'公元前一五四年,吴王刘濞联合楚王刘戊等六王起兵,号称要诛晁错、清君侧。叛军向西猛攻梁国,诸侯割据的旧矛盾终于变成全国战争。汉景帝听从袁盎建议,在长安处死主张削藩的晁错,希望以此换取七国退兵;但吴楚军没有停下。朝廷任命周亚夫为太尉,窦婴为大将军。周亚夫没有直赴梁国救急,而从武关绕行,到昌邑深沟高垒,拒绝同吴楚军正面决战。他派轻骑截断淮泗口,切断叛军粮道。梁国承受正面攻势,吴楚军屡次挑战周亚夫营垒都无法得战,粮食渐尽,只能退却。汉军随后追击,吴楚主力崩溃,其余诸王相继败亡。战争从正月持续到三月便告平定。杀晁错没有换来和平,真正结束叛乱的是中央军的调度、梁国的牵制与被切断的补给。' },
scenes:[
{id:'01',title:'七王举兵',timePlace:'公元前154年正月,吴楚与梁国方向',purpose:'交代叛乱规模与吴楚军首先压向梁国。',narration:'吴王刘濞联合楚王等六王起兵,以诛晁错为名向西推进,梁国首当其冲。',overlay:{heading:'七王举兵',line:'“清君侧”是旗号,诸侯与中央的冲突才是战火。'},people:[{name:'刘濞',office:'吴王、叛军盟主',action:'联合六王起兵并率吴楚军西进'},{name:'刘武',office:'梁王',action:'守梁国,承受吴楚主力进攻'}],image:img('01-seven-kings-rise','吴楚军跨过淮泗水网向梁国推进,吴王刘濞在船车混合队列前观察西方,沿途诸侯兵装束不一。','Western Han rebel host moves west through the Huai-Si river plain. Wu King Liu Bi, an older East Asian prince in practical dark robe over armor, surveys a mixed column of infantry, riders, river boats and supply carts; contingents have varied equipment, showing an alliance rather than one uniform army. Distant fortified Liang settlement braces for attack. No readable rebel slogans.','具体行军道路、舟车比例和刘濞外貌为视觉重构;七王皆反、吴楚攻梁有史载。'),sources:['《汉书·景帝纪》卷五','《史记·吴王濞列传》卷一百六']},
{id:'02',title:'斩错不止兵',timePlace:'长安与关东战报抵达之际',purpose:'呈现晁错被牺牲后叛军仍不退,避免将处决画成血腥奇观。',narration:'景帝斩晁错“以谢七国”。消息传出,吴楚军并未解散,战争仍须由军队解决。',overlay:{heading:'斩错不止兵',line:'一个人的死,没有让七支军队回头。'},people:[{name:'汉景帝',office:'皇帝',action:'下令处死晁错,试图缓和叛乱'},{name:'晁错',office:'御史大夫',action:'因削藩政策被杀;不表现行刑过程'}],image:img('02-chao-cuo-sacrificed','长安宫门外,晁错留下的冠帽与封闭车驾从画面边缘远去,景帝在殿门内接到关东军报,朝臣沉默,不直接呈现腰斩。','Restrained political aftermath at a Western Han palace gate in Chang’an. A closed official carriage and an abandoned dark court cap recede at the far edge, implying Chao Cuo’s execution without showing it. Emperor Jing, a mature East Asian ruler in austere court robes, stands inside the doorway receiving fresh sealed wooden military reports from the east; ministers remain silent. Somber, no execution, no blood, no ornate later palace.','以冠帽、封闭车驾与新军报并置为视觉重构;斩晁错以谢七国及叛军不止见史载。'),sources:['《汉书·景帝纪》卷五','《史记·袁盎晁错列传》卷一百一']},
{id:'03',title:'昌邑坚壁',timePlace:'昌邑,周亚夫军营',purpose:'表现周亚夫避开吴楚锋芒、拒绝急救梁国并等待断粮见效。',narration:'周亚夫从武关出兵,在昌邑深沟高垒。梁国告急,他仍按兵不出,不给吴楚决战机会。',overlay:{heading:'昌邑坚壁',line:'最难的命令不是出击,而是看着盟友苦战仍不出击。'},people:[{name:'周亚夫',office:'太尉、平叛主帅',action:'屯昌邑坚壁不战,控制主战节奏'},{name:'梁国使者',office:'求援人员',action:'多次请求汉军直接救梁'}],image:img('03-zhou-yafu-holds-at-changyi','昌邑汉军营垒内,周亚夫站在深沟木栅后审视军情,梁国使者急切呈报,士卒守位不出,远处吴楚军列阵挑战。','Inside the fortified Han camp at Changyi. Zhou Yafu, a stern middle-aged East Asian commander in restrained Western Han lamellar armor, studies the enemy from behind a deep ditch and timber palisade. An exhausted Liang messenger presents sealed reports and gestures urgently, while Han troops remain at assigned posts instead of charging. Far across open ground Wu-Chu formations challenge the camp. Emphasize disciplined refusal and tension, no cowardice caricature.','沟垒形态、使者姿态与远处挑战为视觉重构;周亚夫坚壁、梁告急而不往有史载。'),sources:['《史记·绛侯周勃世家》卷五十七']},
{id:'04',title:'断粮三月平',timePlace:'淮泗口与吴楚军退路',purpose:'以截粮和撤退收束战争,避免把七国之乱压成一场正面会战。',narration:'汉轻骑截断淮泗口粮道。吴楚军久攻不得战,粮尽退却,汉军追击,三月平定七国。',overlay:{heading:'断粮三月平',line:'营垒没有动,粮道先断,叛军自己失去了时间。'},people:[{name:'周亚夫',office:'太尉',action:'部署轻骑截断吴楚粮道并在其退却后追击'},{name:'吴楚军',office:'叛军主力',action:'因粮绝与攻势受阻而退败'}],image:img('04-supply-line-cut-retreat','淮泗口附近的粮道上,汉轻骑截住叛军粮车并控制渡口,远处吴楚军开始撤退,周亚夫不在一线英雄摆拍。','Wide Huai-Si crossing after Han light cavalry has cut the rebel supply line. Han riders secure a ford and halt wooden grain carts; sacks and draft animals are intact rather than burning. In the distance Wu-Chu columns turn east in weary retreat while a courier carries the result toward Zhou Yafu’s main camp, keeping the victory about logistics rather than a heroic duel. Early spring haze, controlled motion.','渡口、粮车位置、轻骑规模与撤军同框为视觉推演;截淮泗口、吴楚粮绝退败与三月平乱有史载。'),sources:['《史记·绛侯周勃世家》卷五十七','《汉书·景帝纪》卷五']}
],
bgm:{catalogId:'zhanzhen:jingxing#0',title:'井陉之战',version:'仅复用无主唱冷静谋略器乐 stem',status:'pending-export',pathOrUrl:'',usage:'删去韩信主题与歌词,只保留冷钢琴、低弦和一次低鼓;第三幕持续绷紧,第四幕不做凯旋高潮。',reason:'项目无七国之乱专属曲;井陉器乐层的克制谋略感适合周亚夫坚壁断粮,仓库暂无成品音频。',cues:[{from:'00:00',to:'00:21',scene:'01',direction:'低频建立全国性压力。'},{from:'00:21',to:'00:42',scene:'02',direction:'几乎撤空打击乐。'},{from:'00:42',to:'01:03',scene:'03',direction:'冷弦持续,不响应远处挑战。'},{from:'01:03',to:'01:24',scene:'04',direction:'粮道截断处短促落点,随后平静收束。'}]},
sources:[{claim:'七王起兵、斩晁错、遣周亚夫窦婴与三月平乱',citation:'《汉书·景帝纪》卷五',url:'https://zh.wikisource.org/zh-hans/汉书/卷005'},{claim:'周亚夫由武关、屯昌邑、坚壁与绝吴楚粮道',citation:'《史记·绛侯周勃世家》卷五十七',url:'https://zh.wikisource.org/zh-hans/史记/卷057'},{claim:'吴王刘濞举兵与吴楚败亡',citation:'《史记·吴王濞列传》卷一百六',url:'https://zh.wikisource.org/zh-hans/史记/卷106'}],
blacklist:['把七国之乱画成七位诸侯王同场会盟或七军统一指挥','把晁错之死写成叛乱因此停止;史实相反','把周亚夫不救梁写成怯战,忽略其坚壁断粮部署','虚构精确总兵力、单一决战、周亚夫与刘濞单挑','现代文字地图、水印、后世服饰建筑、火器爆炸、血腥行刑']
},
{
dir:'马邑之谋(伏击未发)',
event:{canonicalName:'马邑之谋(伏击未发)',aliases:['马邑之谋','马邑伏击','聂壹诱单于'],era:'汉武帝元光年间',date:'公元前133年(《史记》与《汉书》系年相差一岁)',place:'马邑、武州塞及马邑旁谷(今山西朔州一带)',summary:'汉以聂壹诈降诱军臣单于入塞,数十万伏兵藏于马邑旁谷;单于发现牲畜无人牧的破绽并从被俘尉史处得知伏兵,全军退还。'},
narration:{language:'zh-CN',estimatedSeconds:85,fullText:'公元前一三三年,汉武帝决定把延续多年的和亲改成一次大规模伏击。马邑豪商聂壹向匈奴诈降,声称能杀死县令丞、献出城池。为取信单于,汉方把死罪囚的首级悬在城上。军臣单于率十余万骑穿过武州塞,向马邑深入;汉军车骑、材官数十万人则藏在城旁山谷,等单于入城后合围。《史记》一处记伏兵二十余万,另一处与《汉书》记三十余万,数字应并存。距马邑还有百余里时,单于看见牲畜遍布野外,却没有牧人,立刻起疑。他攻下一座边亭,俘获尉史。尉史供出汉军伏兵位置,单于惊呼险些被汉人出卖,随即北撤。汉军追到边塞,判断追不上,只得停止。负责截击匈奴辎重的王恢也没有出兵,战后以逗留畏缩论罪,自杀而死。马邑一箭未发,却终结了和亲框架,汉匈从此转入长期战争。'},
scenes:[
{id:'01',title:'聂壹诈降',timePlace:'马邑城与匈奴使者往来期间',purpose:'交代诱敌骗局,克制处理悬首取信。',narration:'聂壹诈称能献马邑。汉方以被覆布遮挡的刑具和城门易手假象向单于使者取信。',overlay:{heading:'聂壹诈降',line:'一座城被摆成诱饵,真假只差单于肯不肯再走百里。'},people:[{name:'聂壹',office:'马邑豪商',action:'作为间者向匈奴诈降诱敌'},{name:'匈奴使者',office:'军臣单于使者',action:'查验马邑诈降可信度'}],image:img('01-nie-yi-feigns-surrender','马邑城门内,聂壹向匈奴使者展示已被控制的城防与覆布木架,城门守军刻意稀少,不出现人头特写。','At the gate of frontier Mayi, merchant Nie Yi, a shrewd middle-aged East Asian man in practical wool-hemp travel clothing, receives Xiongnu envoys and gestures toward apparently surrendered defenses. A covered wooden frame high on the wall only implies the historical display of executed prisoners, with no heads or gore visible. Han guards deliberately stay sparse and subdued; tension lies in the staged evidence. Western Han frontier architecture, no readable documents.','覆布处理、使者人数和城门布置为克制视觉重构;聂壹诈降、悬死囚首取信有史载。'),sources:['《史记·韩长孺列传》卷一百八']},
{id:'02',title:'旁谷伏兵',timePlace:'马邑旁谷',purpose:'表现庞大伏兵对隐蔽和单一触发条件的依赖。',narration:'汉军车骑、材官数十万人匿于马邑旁谷,只等单于进入预定范围。',overlay:{heading:'旁谷伏兵',line:'网已经张开,但猎物必须自己走进最窄的位置。'},people:[{name:'韩安国',office:'护军将军',action:'节度马邑主伏兵区诸将'},{name:'李广、公孙贺、李息',office:'骁骑、轻车、材官将军',action:'分领车骑与材官伏兵'}],image:img('02-army-hidden-in-valleys','马邑周边层叠山谷中,汉军步卒、弩手、骑兵和车辆分区隐蔽,韩安国在前景低声部署,谷外道路保持空旷。','Layered dry valleys beside Mayi conceal a large Western Han ambush. Crossbow infantry crouch below ridgelines, cavalry wait behind folds of terrain, and wooden vehicles remain screened under dull cloth and brush. Han Anguo, a mature East Asian commander in practical armor, quietly coordinates officers in the foreground. The road outside the valley remains deliberately empty. Show scale through depth and multiple concealed pockets, not an impossible single mass or camouflage net technology.','具体谷形、各部隐蔽位置与指挥动作是视觉推演;车骑材官数十万匿马邑旁谷有史载。'),sources:['《史记·韩长孺列传》卷一百八','《史记·匈奴列传》卷一百十']},
{id:'03',title:'畜在而人空',timePlace:'距马邑百余里,边亭附近',purpose:'呈现让单于起疑的关键破绽与尉史泄谋。',narration:'单于见牲畜遍野却无人放牧,转而攻亭,俘得尉史。伏兵位置由此泄露。',overlay:{heading:'畜在而人空',line:'不是伏兵露了甲,是一片草场少了牧人。'},people:[{name:'军臣单于',office:'匈奴单于',action:'率十余万骑入塞,发现异常后停止深入'},{name:'雁门尉史',office:'汉边吏',action:'被俘后供出马邑伏兵'}],image:img('03-empty-pasture-raises-suspicion','开阔草场上牛羊散布却看不到牧人,军臣单于勒马观察,匈奴骑队停在后方;远处边亭被包围,尉史被带出但未受刑。','Open pasture about a hundred li from Mayi: cattle and sheep are scattered everywhere, but there is not a single herder. Junchen Chanyu, an experienced Xiongnu leader in restrained steppe lamellar and layered wool, reins in his horse and studies the unnatural emptiness; his large mounted column halts behind him. Farther away, riders surround a small Han frontier watch post and escort a captured clerk out alive, no torture. Suspicion and observation, not combat spectacle.','牲畜分布、单于勒马、亭障距离与尉史被带出为视觉重构;畜布野无人牧、攻亭得尉史有史载。'),sources:['《史记·匈奴列传》卷一百十','《史记·韩长孺列传》卷一百八']},
{id:'04',title:'伏击未发',timePlace:'武州塞与汉军伏击区',purpose:'交代单于退军、汉军无功和王恢结局,不伪造交战。',narration:'单于得知伏兵,立即引军北还。汉军没有追上,王恢也未截辎重,整场伏击无功。',overlay:{heading:'伏击未发',line:'几十万人等来的是撤军号令,也等来了更长的战争。'},people:[{name:'军臣单于',office:'匈奴单于',action:'识破伏击后全师退还'},{name:'王恢',office:'大行、屯将军',action:'未出击匈奴辎重,战后下狱并自杀'},{name:'汉军伏兵',office:'车骑材官',action:'因目标退出伏击圈而无功撤军'}],image:img('04-ambush-stands-down','山谷伏兵远望匈奴骑队越过塞口北撤,汉军将领传达停止追击,士卒收起弩机,双方没有交战画面。','Wide view from the concealed Han valley toward Wuzhou Pass. The Xiongnu mounted column withdraws north in an orderly long line beyond the planned kill zone. In the foreground Han crossbowmen lower and unstring weapons while an officer passes the stand-down order; Wang Hui watches a separate baggage-route approach from a distant ridge, visibly choosing not to attack. No arrows in flight, no battle, the failure is silence and distance.','王恢远望、撤令传递和两军距离为视觉推演;单于引还、汉追不及、王恢罢兵无功有史载。'),sources:['《史记·韩长孺列传》卷一百八']}
],
bgm:{catalogId:'zhanzhen:mayizhimou#0',title:'马邑之谋',version:'电影级配乐·建议使用无主唱器乐 stem',status:'pending-export',pathOrUrl:'',usage:'第一幕保持交易式克制,第二幕堆叠低频而不爆,第三幕突然抽空,第四幕以远去马蹄与低弦收束;删除歌词与品牌尾签。',reason:'项目内马邑专属自生成音乐;军臣单于视角的冷静撤离正适合“未战即败”的结构,仓库暂无音频。',cues:[{from:'00:00',to:'00:21',scene:'01',direction:'少量木质敲击与低音。'},{from:'00:21',to:'00:42',scene:'02',direction:'逐层堆叠伏兵压力。'},{from:'00:42',to:'01:03',scene:'03',direction:'在发现空牧场时骤然收空。'},{from:'01:03',to:'01:24',scene:'04',direction:'马蹄远去,不给战斗高潮。'}]},
sources:[{claim:'聂壹诈降、伏兵异文、尉史泄谋、王恢罢兵与自杀',citation:'《史记·韩长孺列传》卷一百八',url:'https://zh.wikisource.org/zh-hans/史记/卷108'},{claim:'单于入塞、畜布野无人牧与和亲断绝',citation:'《史记·匈奴列传》卷一百十',url:'https://zh.wikisource.org/zh-hans/史记/卷110'}],
blacklist:['把马邑之谋画成已经发生的大规模会战;史实是伏击未发、双方无功','把伏兵数固定成唯一准确数字;史记二十余万与三十余万异文应并存','展示悬首、刑讯或王恢自杀的血腥近景','把单于识破全归功于尉史,忽略先见畜布野无人牧而起疑','现代文字地图、水印、后世服饰建筑、火器爆炸或动漫画风']
},
{
dir:'陈汤灭郅支(虽远必诛)',
event:{canonicalName:'陈汤灭郅支(虽远必诛)',aliases:['陈汤灭郅支','郅支城之战','郅支单于之战'],era:'汉元帝建昭三年',date:'公元前36年',place:'康居东界都赖水畔郅支城(约今哈萨克斯坦塔拉斯河流域,城址为后世推定)',summary:'西域副校尉陈汤矫制发兵,迫使都护甘延寿共同率汉胡联军四万余远征郅支城;联军破木城、围土城,杀郅支单于。'},
narration:{language:'zh-CN',estimatedSeconds:89,fullText:'公元前三十六年,郅支单于远据康居东部,曾杀汉使谷吉,又多次困辱汉使。西域副校尉陈汤判断,郅支虽远,却没有汉地坚城强弩,若各国兵同时出发,一朝可以攻取。西域都护甘延寿主张先上奏朝廷,陈汤担心不会获准,趁甘延寿久病,矫造诏令,调发西域城郭诸国兵与屯田吏士。甘延寿惊起阻止,陈汤按剑斥责,最终迫使他共同出兵。汉兵与诸国兵合计四万余,分六校穿越西域,抵达都赖水畔的郅支城。联军四面围城,挖壕堵门,以盾牌在前、戟弩在后压制城楼。外层木城被薪火烧穿,郅支退入土城。天明,四面火起,联军乘势入城。郅支负伤而死,军候假丞杜勋斩其首。甘延寿、陈汤上疏自劾,也写下“明犯强汉者,虽远必诛”。朝廷既嘉其功,又忌其擅权,最终封赏远低于这场万里远征的声势。' },
scenes:[
{id:'01',title:'矫制发兵',timePlace:'西域都护府,甘延寿病中',purpose:'呈现陈汤擅发诸国兵的风险与甘延寿被迫同意。',narration:'甘延寿想先奏请,陈汤却趁其久病矫制发兵。甘延寿欲止,陈汤按剑迫其同行。',overlay:{heading:'矫制发兵',line:'远征从一道未经朝廷批准的命令开始。'},people:[{name:'陈汤',office:'西域副校尉',action:'矫制调发诸国兵与屯田吏士'},{name:'甘延寿',office:'西域都护、骑都尉',action:'原欲奏请,最终在陈汤压力下共同出兵'}],image:img('01-forged-order-at-protectorate','西域都护府内,病中的甘延寿从榻上惊起,陈汤按着入鞘佩剑站在封闭木牍旁,几名军吏等待命令,不出现可读诏书。','Inside a modest Western Han Protectorate office in the Western Regions. Gan Yanshou, a seasoned East Asian commander weakened by illness, rises from a low couch in alarm. Chen Tang, a sharp-eyed East Asian officer in his thirties wearing travel-worn Han armor and robe, stands over sealed blank wooden-slip bundles with one hand resting on a sheathed sword, forcing the decision without drawing it. Several clerks and allied envoys wait tensely. Political coercion, no assault, no readable edict.','病榻、木牍、军吏和按剑姿态为视觉重构;久病、矫制发兵、甘惊欲止与陈汤按剑叱有史载。'),sources:['《汉书·傅常郑甘陈段传》卷七十']},
{id:'02',title:'六校万里西征',timePlace:'西域诸国至康居东界',purpose:'表现汉胡联军四万余的多国组成与远征尺度。',narration:'汉兵与西域诸国兵合四万余,编为六校,穿过城郭、荒漠与草原,直指康居东界。',overlay:{heading:'六校西征',line:'这不是长安大军出塞,而是西域兵力被临时汇成一路。'},people:[{name:'甘延寿',office:'西域都护、法定主帅',action:'部勒联军行阵'},{name:'陈汤',office:'西域副校尉、谋主',action:'推动并组织远征'},{name:'城郭诸国兵与屯田吏士',office:'汉胡联军',action:'组成四万余人的六校远征军'}],image:img('02-six-columns-march-west','中亚荒漠草原与绿洲交界处,甘延寿、陈汤率多种服饰但装备协调的六路队伍西进,汉军弩手、屯田兵和城郭诸国骑兵分层行军。','Vast Central Asian steppe and oasis frontier. Gan Yanshou and Chen Tang lead a multi-ethnic allied army west in six separated columns visible across the landscape: Han crossbow infantry and tuntian troops mixed with cavalry from Western Region city-states, varied faces and clothing but coordinated movement. Camels, horses and modest supply carts show distance and logistics. No modern ethnic stereotypes, no Silk Road caravan fantasy, no readable flags.','六校在同一远景中的位置、兵种与绿洲地貌为视觉推演;汉胡兵合四万余、分六校进军有史载。'),sources:['《汉书·傅常郑甘陈段传》卷七十']},
{id:'03',title:'四面薄城',timePlace:'都赖水畔郅支城',purpose:'呈现土城外重木城、壕沟堵门与盾戟弩协同攻坚。',narration:'联军闻鼓四面薄城,挖壕堵门。盾牌在前、戟弩在后,压住城楼射手。',overlay:{heading:'四面薄城',line:'远征的最后三里,变成一层木城和一层土城。'},people:[{name:'甘延寿',office:'联军主帅',action:'部勒行阵并令各军闻鼓薄城'},{name:'陈汤',office:'副校尉',action:'参与四面围城与攻坚指挥'},{name:'郅支单于',office:'匈奴单于',action:'披甲登楼督战'}],image:img('03-surround-zhizhi-city','都赖水旁,联军四面包围外木栅内土城,前排持大盾、后排戟弩仰射,士卒开挖壕沟封堵城门,郅支只在楼上远景督战。','Zhizhi’s fortified city beside the Talas-like Dulai River: an outer heavy timber palisade surrounds an inner rammed-earth fortress. Allied troops close from multiple sides, large shields forward and crossbows with long weapons behind, while work parties dig trenches and block gates. Gan Yanshou and Chen Tang coordinate from a protected middle position. Zhizhi Chanyu appears only as a distant armored figure on the tower. Clear defensive layers, no medieval stone castle, no siege cannon or fantasy machinery.','城防外观、四面军队同框、盾戟弩队形与壕沟位置为视觉重构;木城土城、穿堑塞门、卤楯戟弩有史载。'),sources:['《汉书·傅常郑甘陈段传》卷七十']},
{id:'04',title:'火穿木城',timePlace:'郅支城,夜至平明',purpose:'表现火攻外层木城与进入土城,不渲染后宫参战和斩首。',narration:'联军发薪烧穿木城。平明四面火起,鼓声动地,攻军乘火进入土城。',overlay:{heading:'火穿木城',line:'外层木栅先失守,孤城的纵深只剩最后一层。'},people:[{name:'陈汤、甘延寿',office:'联军统帅',action:'指挥火攻木城并推进土城'},{name:'郅支单于',office:'守城主帅',action:'负伤后退入内室,最终死于城破'}],image:img('04-fire-breaches-wooden-wall','黎明前木城多点燃烧但无爆炸,联军盾阵从破口推进,内侧土城仍在抵抗;郅支受伤撤离只以远处被搀扶身影表现。','Before dawn, controlled fires burn through several sections of the outer timber wall without explosions. Western Han and allied shield teams advance through smoking breaches while crossbowmen cover them; the inner rammed-earth fortress still resists. A distant armored Zhizhi Chanyu is helped down from the tower after being wounded, never shown in close-up and no death or severed head. Firelight, smoke and drum-driven pressure, not a fantasy inferno.','火点、破口、盾阵和郅支远景撤下为视觉重构;发薪烧木城、平明四面火起与郅支负创有史载。'),sources:['《汉书·傅常郑甘陈段传》卷七十']},
{id:'05',title:'功成自劾',timePlace:'战后郅支城与上疏回长安',purpose:'以战后清点、封缄首级和自劾奏疏收束功劳与擅权并存。',narration:'郅支被杀,杜勋取得其首。甘延寿、陈汤封缄战果,上疏自劾,也写下“虽远必诛”。',overlay:{heading:'功成自劾',line:'同一封奏疏里,一边是万里奇功,一边是矫诏之罪。'},people:[{name:'甘延寿',office:'西域都护',action:'与陈汤共同上疏自劾并报告战果'},{name:'陈汤',office:'西域副校尉',action:'提出远征并写下“明犯强汉者,虽远必诛”'},{name:'杜勋',office:'军候假丞',action:'斩郅支首;本幕只呈封闭木匣,不展示首级'}],image:img('05-victory-and-self-accusation','战后营帐内,甘延寿与陈汤面对封缄木匣和无字奏牍,杜勋站在一旁,城郭诸国将领在远处清点俘虏,画面不展示头颅或可读名句。','Restrained aftermath inside the allied camp. Gan Yanshou and Chen Tang sit across a low table holding sealed blank memorial slips and a closed wooden evidence box; their faces show relief mixed with awareness of unauthorized command. Du Xun stands nearby after reporting the result. Outside, officers from the Western Region city-states calmly register prisoners and distribute supplies, no humiliation. The famous sentence is represented only in metadata, never visible as calligraphy. No severed head.','奏牍内容、木匣封缄、人物神态与清点场景为视觉重构;杜勋斩首、两将上疏自劾及名句有史载。'),sources:['《汉书·傅常郑甘陈段传》卷七十']}
],
bgm:{catalogId:'zhanzhen:chentangmiezhizhi#0',title:'陈汤灭郅支',version:'电影级配乐·建议使用无主唱器乐 stem',status:'pending-export',pathOrUrl:'',usage:'第一幕以按住不发的低弦起,第二幕拓宽空间,第三幕建立攻坚节拍,第四幕为唯一高潮,第五幕撤鼓转为克制政治余韵;删除歌词与品牌尾签。',reason:'项目内郅支之战专属自生成音乐,适配矫制—远征—攻城—自劾结构;仓库暂无音频。',cues:[{from:'00:00',to:'00:18',scene:'01',direction:'窄声场和低弦。'},{from:'00:18',to:'00:36',scene:'02',direction:'增加长线行军空间。'},{from:'00:36',to:'00:54',scene:'03',direction:'盾弩推进对应稳重节拍。'},{from:'00:54',to:'01:12',scene:'04',direction:'木城破口形成唯一高潮。'},{from:'01:12',to:'01:30',scene:'05',direction:'撤去鼓点,以一声低音收束功罪。'}]},
sources:[{claim:'矫制发兵、六校远征、四万余联军、围城攻坚、斩郅支与上疏封赏',citation:'《汉书·傅常郑甘陈段传》卷七十',url:'https://zh.wikisource.org/zh-hans/汉书/卷070'},{claim:'郅支单于身份、入康居与呼韩邪后续',citation:'《汉书·匈奴传》卷九十四',url:'https://zh.wikisource.org/zh-hans/汉书/卷094'}],
blacklist:['把陈汤矫制写成得到汉元帝预先批准;它正是战后争议核心','把甘延寿抹去;他是法定主帅且参与实际攻城指挥','把郅支城画成欧洲中世纪石堡,或断言城址就是现代塔拉兹唯一确定地点','展示郅支首级、斩首、火烧人群和后宫死亡近景','把“虽远必诛”生成在画面旗帜、城墙或奏牍上','现代文字地图、水印、后世服饰、火器爆炸、奇幻或动漫画风']
}
]
function md(p){const a=[`# ${p.event.canonicalName}|素材说明`,'','## 事件边界','',`- 规范事件名:${p.event.canonicalName}`,`- 常用称呼:${p.event.aliases.join('、')}`,`- 时代:${p.event.era}`,`- 日期:${p.event.date}`,`- 地点:${p.event.place}`,`- 一句话结论:${p.event.summary}`,'','## 60–90 秒主旁白','',p.narration.fullText,'','## 分幕素材',''];for(const s of p.scenes)a.push(`### ${s.id}|${s.title}`,'',`- 时间地点:${s.timePlace}`,`- 本幕作用:${s.purpose}`,`- 分幕旁白:${s.narration}`,`- 页面标题:${s.overlay.heading}`,`- 页面短句:${s.overlay.line}`,`- PNG:\`${s.image.original}\``,`- WebP:\`${s.image.delivery}\``,`- 替代文本:${s.image.alt}`,`- 视觉推演边界:${s.image.visualInference}`,`- 人物:${s.people.map(x=>`${x.name}(${x.office};${x.action})`).join(';')}`,`- 史料:${s.sources.join(';')}`,'','生成提示词:','','```text',s.image.prompt,'```','');a.push('## BGM 选曲','',`- 项目曲目:\`${p.bgm.catalogId}\`|${p.bgm.title}|${p.bgm.version}`,`- 状态:${p.bgm.status}`,`- 用法:${p.bgm.usage}`,`- 选择理由:${p.bgm.reason}`,'- 音频路径或 URL:仓库暂缺,待导出','','| 时间 | 分幕 | 编排 |','|---|---|---|',...p.bgm.cues.map(x=>`| ${x.from}–${x.to} | ${x.scene} | ${x.direction} |`),'','## 史料来源','',...p.sources.map(x=>`- ${x.claim}:[${x.citation}](${x.url})`),'','## 禁区与误读防护','',...p.blacklist.map(x=>`- ${x}`),'','## 交付检查','','- [ ] 每幕 PNG 原图存在且为 16:9','- [ ] 每幕 WebP 交付图存在且为 16:9','- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全','- [ ] BGM 已导出并填写可用路径','- [ ] `manifest.json` 与本说明中的文件名全部校验通过','');return a.join('\n')+'\n'}
for(const p of packs){const d=path.join(outRoot,p.dir);if(fs.existsSync(d))throw new Error(`Refusing to overwrite ${d}`);fs.mkdirSync(path.join(d,'images'),{recursive:true});fs.writeFileSync(path.join(d,'manifest.json'),JSON.stringify({event:p.event,narration:p.narration,scenes:p.scenes,bgm:p.bgm,sources:p.sources,blacklist:p.blacklist},null,2)+'\n');fs.writeFileSync(path.join(d,'素材说明.md'),md(p))}
console.log(`Generated ${packs.length} Western Han empire packages`)
+45
View File
@@ -0,0 +1,45 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材', '新')
const BASE = `Premium live-action Chinese historical film still, archaeology-informed Xin dynasty and late Western Han material culture, early first century CE, cinematic 16:9 landscape, subtle film grain, physically plausible scale, rammed earth, aged timber, hemp, low-sheen silk, leather, lacquered lamellar armor, straight swords, crossbows and wooden carts. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, Tang/Song/Ming/Qing gear or architecture, imperial yellow dragon robes, Forbidden City, modern maps, UI arrows, firearms, cannons, gunpowder explosions, samurai armor, European plate, magic, anime, gore or body piles.`
const img = (file, alt, request, inference) => ({ original:`images/${file}.png`, delivery:`images/${file}.webp`, alt, prompt:`Use case: historical-scene. Digital Me Xin-dynasty story frame. ${request} ${BASE}`, visualInference:inference, refinementPrompt:'' })
const packs = [
{
dir:'成昌之战(廉丹战死)',
event:{canonicalName:'成昌之战(廉丹战死)',aliases:['成昌之战','廉丹战死','王匡廉丹东征赤眉'],era:'新朝地皇三年',date:'公元22年四月至冬',place:'东方诸郡、无盐与成昌(约今山东、河南交界区域;成昌具体位置不作唯一断言)',summary:'王莽派太师王匡、更始将军廉丹率十余万锐士东征;官军攻拔无盐后疲兵冒进成昌,被赤眉别校董宪所部击败,王匡逃走,廉丹交出印绶符节后留下战死。'},
narration:{language:'zh-CN',estimatedSeconds:87,fullText:'公元二十二年四月,王莽派太师王匡、更始将军廉丹率十余万锐士东征赤眉。出长安都门时大雨湿衣,长老叹息说这是“泣军”。官军一路放纵,东方百姓甚至传唱:宁可遇到赤眉,也不要遇到太师和更始将军。入冬,无盐的索卢恢等举兵据城,王匡、廉丹攻下无盐,斩首万余。王莽遣使慰劳,两人都进爵为公。赤眉别校董宪率数万人在梁郡,王匡想乘胜追击。廉丹认为军队刚刚攻城,已经疲惫,应先休兵养威;王匡不听,独自引军前进,廉丹只能跟随。两军在成昌交战,新军战败,王匡逃走。廉丹把印绶和符节交给属吏送还王匡,说他可以走,自己不能走,随后留下战死。汝云、王隆等二十余名校尉听说廉丹已死,也返身赴战。这场败仗没有复杂的计谋,只有一支失去民心的疲军、一次不被采纳的劝阻,以及两位主帅在败局中截然不同的选择。'},
scenes:[
{id:'01',title:'雨湿泣军',timePlace:'公元22年四月,长安都门外',purpose:'从出师大雨和东方民谣建立官军失去民心的背景。',narration:'王匡、廉丹率十余万锐士东征。出都门大雨湿衣,长老叹为“泣军”。',overlay:{heading:'雨湿泣军',line:'大军尚未接敌,百姓已经替它写下不祥的名字。'},people:[{name:'王匡',office:'新朝太师',action:'与廉丹率十余万锐士东征'},{name:'廉丹',office:'新朝更始将军',action:'共同统军东进'},{name:'长安长老',office:'出师旁观者',action:'见大雨湿军,叹称“泣军”'}],image:img('01-weeping-army-departs','长安都门外阴雨中,王匡和廉丹立马检视出征队列,十余万军只以层层延伸的步骑与粮车暗示,一名老者在路旁望着湿透士卒叹息。','Cold rain outside an austere Chang’an gate. Wang Kuang, an older high official turned field commander, and Lian Dan, a seasoned commander in his fifties with a square weathered face, practical dark lamellar armor and a muted red-brown cloak, inspect sodden ranks departing east. Infantry, riders and baggage carts stretch in layers without impossible crowd scale. At the roadside, one elderly civilian watches soaked soldiers and quietly laments. Somber departure, no supernatural omen, no readable standards.','人物外貌、雨中队列与长老位置为视觉重构;四月出师、大雨湿衣、长老称“泣军”有史载。'),sources:['《汉书·王莽传下》卷九十九下']},
{id:'02',title:'拔无盐进爵',timePlace:'公元22年冬,无盐城',purpose:'交代官军先胜和两将进爵,避免成昌失败显得毫无前因。',narration:'入冬,官军攻拔索卢恢据守的无盐,斩首万余。王莽遣使劳军,两将进爵为公。',overlay:{heading:'拔无盐进爵',line:'胜利和封赏,让疲惫看起来像还可以再追一次。'},people:[{name:'王匡',office:'太师',action:'与廉丹攻拔无盐,获进爵为公'},{name:'廉丹',office:'更始将军',action:'参与攻城并接受劳军封赏'},{name:'索卢恢',office:'无盐起事首领',action:'据城反新;城破后的个人结局不在画面断言'}],image:img('02-wuyan-falls-and-envoy-arrives','无盐土城被攻破后的克制场景,新军收拢俘虏与器械,疲惫的王匡、廉丹在城门外接过使者带来的封缄诏牍,不展示首级或庆功宴。','Restrained aftermath outside captured Wuyan, a modest rammed-earth county town. Xin troops secure the gate, gather intact weapons and register prisoners without humiliation. Wang Kuang and Lian Dan, matching scene 01 and visibly exhausted, receive sealed blank wooden-slip orders from an imperial courier beside mud-stained horses. The official reward arrives before the army has rested. No severed heads, no feast, no massacre, no readable edict.','城墙、收拢俘虏、使者抵达与两将受命同框为视觉推演;攻拔无盐、斩首万余、遣使劳军与进爵有史载。'),sources:['《汉书·王莽传下》卷九十九下']},
{id:'03',title:'疲军仍进',timePlace:'无盐至梁郡成昌方向',purpose:'呈现廉丹劝休、王匡不听这一直接决策节点。',narration:'董宪数万人在梁郡。廉丹请先休士养威,王匡不听,独自引军前进,廉丹随后跟上。',overlay:{heading:'疲军仍进',line:'该停的时候没有停,前一场胜利成了下一场败仗的起点。'},people:[{name:'王匡',office:'太师、主帅',action:'拒绝休整建议,引兵进击董宪'},{name:'廉丹',office:'更始将军',action:'主张休士养威,劝阻不成后随军前进'},{name:'董宪',office:'赤眉别校',action:'率数万人在梁郡活动'}],image:img('03-lian-dan-urges-rest','泥泞行军营地中,廉丹按住展开的无字木牍并指向疲兵与倒卧马匹,劝王匡休整;王匡已翻身上马指向前路,队列被迫重新集合。','Muddy roadside camp after Wuyan. Lian Dan, matching prior scenes, presses one hand beside blank wooden route slips and points to exhausted infantry, damaged sandals, tired horses and unfinished cooking fires, urging rest. Wang Kuang has already mounted and gestures toward the road to Liang commandery, impatient to advance. Officers reluctantly call worn troops back into line. The disagreement is practical, not theatrical shouting; no map text.','议军地点、疲兵细节、两将手势和行军重启为视觉重构;廉丹劝休、王匡不听独进、廉丹随之有史载。'),sources:['《汉书·王莽传下》卷九十九下','《后汉纪》卷一']},
{id:'04',title:'成昌兵败',timePlace:'成昌战场',purpose:'表现疲军交战后崩溃,不虚构阵法、单挑或精确伤亡。',narration:'新军与董宪所部合战成昌,阵线很快崩溃。王匡带兵撤走,廉丹被留在败局中央。',overlay:{heading:'成昌兵败',line:'史书没有留下阵图,只留下“匡走”与“丹止”。'},people:[{name:'董宪',office:'赤眉别校',action:'率赤眉数万人与新军交战'},{name:'王匡',office:'太师',action:'兵败后逃走'},{name:'廉丹',office:'更始将军',action:'在败阵中停止撤退'}],image:img('04-xin-line-breaks-at-chengchang','冬日平野上新军阵线破裂,赤眉军以眉部红色涂记相互识别但不戴统一红头巾;王匡带少数骑从远撤,廉丹在中景令部曲收拢盾阵。','Winter battle on an open eastern plain near Chengchang. The exhausted Xin formation breaks into retreat under pressure from Dong Xian’s Red Eyebrow forces, whose fighters use small red marks painted around their brows for recognition rather than theatrical red headbands. In the far middle distance Wang Kuang withdraws with a small mounted group. Lian Dan remains closer to the broken line, ordering a compact shield group to turn and hold. No invented duel, no precise tactic claim, no corpses or blood.','阵线、两将相对位置、赤眉识别方式与撤退方向为视觉重构;成昌合战、兵败、王匡走、廉丹止有史载。'),sources:['《汉书·王莽传下》卷九十九下','《后汉纪》卷一']},
{id:'05',title:'交印而止',timePlace:'成昌败阵后段',purpose:'以印绶符节和返身赴战收束廉丹选择,克制处理战死。',narration:'廉丹把印绶符节交给属吏送还王匡,自己留下。二十余名校尉得知后,也返身赴战。',overlay:{heading:'交印而止',line:'印已经交出去,他仍没有走。'},people:[{name:'廉丹',office:'更始将军、果公',action:'交出印绶符节后留下战死'},{name:'汝云、王隆等',office:'校尉二十余人',action:'闻廉丹死,返身赴战并战死'},{name:'王匡',office:'太师',action:'接收廉丹送还的印绶符节;本人已撤离'}],image:img('05-seal-returned-last-stand','暮色败阵边缘,廉丹把装有印绶符节的封闭木匣交给骑马属吏令其追送王匡,自己转身走向尚在集结的二十余名校尉;不展示死亡瞬间。','At the edge of the defeated field in fading winter light, Lian Dan, matching prior scenes, hands a mounted clerk a closed wooden box containing his seal ribbons and command tally, directing him after the retreating Wang Kuang. Lian Dan then turns back toward a small group of roughly two dozen officers reforming around battered shields. Show resolve and exhaustion, not a glamorous last-charge pose. No death moment, no blood, no readable inscription.','木匣、传递动作、校尉集结和暮色为克制视觉重构;交印绶符节、廉丹止而战死、汝云王隆等返战皆有史载。'),sources:['《汉书·王莽传下》卷九十九下']}
],
bgm:{catalogId:'zhanzhen:kunyang#1',title:'昆阳之战',version:'王邑版·仅复用无主唱新莽败军器乐 stem',status:'pending-export',pathOrUrl:'',usage:'删除王邑歌词与昆阳专属风雨音效,保留低弦、沉重军鼓和逐步松散的节拍;第二幕短促抬升,第三幕起不断失稳,第五幕撤去主鼓。',reason:'项目没有成昌专属曲;王邑版同属新莽败军视角,可复用为王匡、廉丹东征的制度性崩解声场,仓库暂无成品音频。',cues:[{from:'00:00',to:'00:17',scene:'01',direction:'雨声感与低鼓建立泣军。'},{from:'00:17',to:'00:34',scene:'02',direction:'短促抬升但不凯旋。'},{from:'00:34',to:'00:51',scene:'03',direction:'节拍开始不稳。'},{from:'00:51',to:'01:08',scene:'04',direction:'崩阵处打击层散开。'},{from:'01:08',to:'01:27',scene:'05',direction:'撤鼓,仅留低弦与木质敲击。'}]},
sources:[{claim:'泣军、十余万锐士、东方民谣、无盐胜利、成昌败战与廉丹交印战死',citation:'《汉书·王莽传下》卷九十九下',url:'https://zh.wikisource.org/zh-hans/汉书/卷099下'},{claim:'董宪在梁郡、劝休未从、成昌败战及校尉返战',citation:'《后汉纪》卷一',url:'https://zh.wikisource.org/zh-hans/后汉纪/卷1'}],
blacklist:['把太师王匡误认成绿林军同名将领王匡','把“更始将军”廉丹误认成更始帝刘玄的将领;这是新朝官号','把无盐胜利与成昌失败合成同一场战斗,或省略疲兵冒进的先后关系','用演义情节虚构董宪与王匡、廉丹单挑或精确阵法','把赤眉画成统一红巾军;其识别标记是涂红眉部','展示斩首万余、廉丹死亡或尸体的血腥近景','现代文字地图、水印、后世服饰建筑、火器爆炸或动漫画风']
},
{
dir:'长安之战(王莽死于渐台)',
event:{canonicalName:'长安之战(王莽死于渐台)',aliases:['新亡·王莽死于渐台','长安城破','渐台之战'],era:'新朝地皇四年',date:'公元23年十月初一至初三(《汉书》纪日戊申朔至庚戌)',place:'长安宣平门、未央宫与渐台',summary:'各路汉兵攻入长安,王邑率残部抵抗;王莽在群臣扶掖下退至四面环水的渐台,十月初三众兵登台,商人杜吴杀王莽并取其绶,新朝灭亡。'},
narration:{language:'zh-CN',estimatedSeconds:89,fullText:'公元二十三年十月,昆阳败后三个多月,各路汉兵从四面会集长安,争先攻城。十月初一,兵从宣平门攻入。城中少年朱弟、张鱼等纵火攻宫门,火势逼近掖庭,王莽从宣室躲避。大司马王邑仍在昼夜苦战,士卒伤亡几乎殆尽;他退入宫中,看见儿子王睦正脱下冠服准备逃走,便喝令他回去,父子最终共同守莽。十月初三清晨,群臣扶着王莽走出白虎门。和新公王揖备车,王莽乘车退往渐台,想利用池水拒守。他仍带着皇帝的玺绶、匕首、符命和威斗,公卿、侍中、黄门郎等一千余人跟上这座水中高台。众兵层层包围,台上仍在抵抗。下午,攻军登台。杀死王莽的不是哪位名将,而是闯上台的商人杜吴。他拿走王莽的绶带,却说不出绶的主人在哪里。新朝十五年的官爵与符命,到此变成一件连夺取者都未必认得的物件。',},
scenes:[
{id:'01',title:'四兵会城',timePlace:'公元23年十月初,长安城外',purpose:'建立昆阳战后三个月长安被各路兵马围攻的终局态势。',narration:'各路汉兵从四面会集长安,争先攻城。新朝已经没有足够的野战军来解围。',overlay:{heading:'四兵会城',line:'昆阳丢掉的是主力,长安等来的是所有方向的兵。'},people:[{name:'更始诸军',office:'攻新联军',action:'从长安四面会集并争先攻城'},{name:'王邑',office:'新朝大司马',action:'组织城内残余兵力防守'}],image:img('01-armies-converge-on-changan','秋日长安外城远景,多路装备不一的更始军从不同道路与城门方向会集,城上新军守卒稀疏,王邑在墙内检查防线,不出现现代鸟瞰地图。','Wide autumn view of early-imperial Chang’an under converging attack. Several distinct columns of Gengshi-aligned troops approach from different roads and gate sectors, with varied equipment showing a coalition rather than one uniform army. Sparse Xin defenders hold rammed-earth walls and timber gate towers. Inside the wall, Wang Yi, an exhausted older commander in battered dark lamellar armor and muted crimson cloak, inspects the defense. Geography implied through depth, not a map or impossible aerial panorama.','兵路、城防视角、各军装备差异与王邑位置为视觉重构;长安旁兵四会城下与争欲先入有史载。'),sources:['《汉书·王莽传下》卷九十九下']},
{id:'02',title:'宣平门入',timePlace:'十月初一,宣平门至未央宫',purpose:'表现破城与宫门火攻,不把城市毁灭当成视觉奇观。',narration:'十月初一,攻军从宣平门入城。朱弟、张鱼等焚烧宫门,王莽避火转入宣室。',overlay:{heading:'宣平门入',line:'城门一破,未央宫的殿门也成了下一层防线。'},people:[{name:'朱弟、张鱼等',office:'长安城中少年',action:'焚烧作室门并呼王莽出降'},{name:'王莽',office:'新朝皇帝',action:'避开宫门火势转入宣室'}],image:img('02-xuanping-gate-breached','长安宣平门被攻开后,士卒与城中少年穿过烟雾进入宽阔街道,远处未央宫外围一扇木门燃烧;王莽只以被近臣护送远离火势的深青衣身影出现。','After the Xuanping Gate is breached, mixed soldiers and Chang’an youths move through smoke into a broad early-imperial street. Farther inside, one timber outer gate of the Weiyang Palace complex burns, orange light limited and realistic rather than a city-wide inferno. Wang Mang appears only as a distant elderly East Asian ruler in dark blue-black formal robes, escorted away from the fire by attendants. No massacre, no giant palace skyline, no readable shouted slogans.','城门破口、火势范围、街道与王莽撤移同框为视觉重构;宣平门入、朱弟张鱼等烧作室门、莽避火宣室有史载。'),sources:['《汉书·王莽传下》卷九十九下']},
{id:'03',title:'衣冠欲逃',timePlace:'未央宫内,王邑退入之际',purpose:'以王邑喝回脱冠服的儿子呈现新朝最后的位阶束缚。',narration:'王邑昼夜苦战,部众几尽。退入宫中时,他撞见儿子王睦正脱冠服逃命,便喝令他回去。',overlay:{heading:'衣冠欲逃',line:'儿子想脱掉的是官服,父亲让他穿回的是最后的身份。'},people:[{name:'王邑',office:'新朝大司马',action:'苦战后喝令儿子王睦返回,共守王莽'},{name:'王睦',office:'侍中、王邑之子',action:'试图解下衣冠逃走,被父亲喝回'}],image:img('03-wang-yi-stops-his-son','烟尘弥漫的宫廊内,甲胄破损的王邑扶墙走入,正看见儿子王睦把官冠和外袍放在木栏旁准备混入人群;王邑抬手喝止,不展示争斗。','Smoke-dim palace corridor during the collapse. Wang Yi, matching scene 01, armor battered and exhausted after continuous fighting, braces against a timber post. He catches his adult son Wang Mu quietly setting down an official cap and outer court robe beside a low rail, preparing to disappear among fleeing attendants. Wang Yi raises one hand and orders him back; the son freezes between identities. Intimate moral pressure, no physical violence, no readable insignia.','宫廊、逃散人流、衣冠摆放与父子动作是视觉重构;王邑见王睦解衣冠欲逃并叱令还、父子共守有史载。'),sources:['《汉书·王莽传下》卷九十九下']},
{id:'04',title:'抱符命上渐台',timePlace:'十月初三清晨,白虎门至渐台',purpose:'呈现王莽携符命威斗退保水中高台及千余从官随行。',narration:'群臣扶王莽出白虎门,王揖备车。王莽抱着符命与威斗退至渐台,一千余从官跟随。',overlay:{heading:'抱符命上渐台',line:'把他送上皇位的符命,最后也被他抱上绝路。'},people:[{name:'王莽',office:'新朝皇帝',action:'携玺绶、符命和威斗退保渐台'},{name:'王揖',office:'和新公',action:'在白虎门外奉车接应'},{name:'公卿与从官',office:'公卿大夫、侍中、黄门郎等',action:'一千余人随王莽登台'}],image:img('04-wang-mang-reaches-jantai','灰冷清晨,年老王莽被群臣扶下低阶、登上白虎门外等候的朴素车驾,怀中抱封闭符命木匣与青铜式盘;前方池水环绕的渐台上已有守卒和上千从官涌入。','Cold gray dawn at the White Tiger Gate. The elderly Wang Mang in dark blue-black robes and seal ribbons is supported down low steps by ministers toward a modest waiting carriage held by Duke Wang Yi. He clutches a closed wooden box representing prophetic documents and a bronze cosmological divination board, neither glowing nor readable. Beyond, a high timber-and-earth terrace stands within palace ponds; streams of court officials and guards crowd onto its narrow approaches. Historically sober, no mystical effects, no monumental fantasy tower.','车驾、符命匣、威斗外观、池水尺度与千余人流为视觉重构;白虎门奉车、莽之渐台、抱符命威斗及从官千余有史载。'),sources:['《汉书·王莽传下》卷九十九下']},
{id:'05',title:'杜吴取绶',timePlace:'十月初三下午,渐台',purpose:'以绶带易手表现王莽之死与新亡,不展示尸体分裂。',narration:'众兵攻上渐台。商人杜吴杀王莽并取走绶带,却答不出绶主所在。新朝至此灭亡。',overlay:{heading:'杜吴取绶',line:'一个王朝最后留下的,是一条夺取者也未必认得的绶带。'},people:[{name:'杜吴',office:'商人、攻台者',action:'杀王莽并取其绶;画面只表现战后持绶'},{name:'公宾就',office:'校尉、故大行治礼',action:'询问杜吴绶带主人所在'},{name:'王莽',office:'新朝皇帝',action:'死于渐台;画面不展示遗体、斩首或分尸'}],image:img('05-du-wu-holds-the-seal-ribbon','战斗刚停的渐台上,商人杜吴衣着朴素、手持从地上拾起的深色绶带,茫然面对前来询问的校尉公宾就;远处封闭室门与放下武器的士卒暗示王莽已死,绝不展示尸体。','Restrained aftermath on the Jantai terrace. Du Wu, an ordinary middle-aged merchant in plain hemp clothing among the attackers, holds a dark imperial seal ribbon he has taken, looking uncertain as the uniformed officer Gongbin Jiu questions him. Behind them, soldiers lower weapons near a closed room door; Wang Mang’s death is implied only by the abandoned carriage gear and silent court objects, with no body, head, blood or dismemberment. Palace ponds and smoke-gray Chang’an beyond, sober end of a regime, not triumph.','杜吴外貌、绶带颜色、问答位置与封闭室门为视觉重构;杜吴杀莽取绶、公宾就询问绶主有史载。'),sources:['《汉书·王莽传下》卷九十九下']}
],
bgm:{catalogId:'mingchangmian:wangmangjiajiuxi#0',title:'王莽·加九锡',version:'电影级配乐·仅取无主唱器乐 stem并反向收束',status:'pending-export',pathOrUrl:'',usage:'删除歌词与加九锡庆典声场,把同一主题由完整礼乐逐幕拆散:城破后只剩低弦、木质敲击和不稳定铜音,第五幕在绶带易手处完全失去主旋律。',reason:'项目没有长安破城专属曲;王莽主题从加九锡到渐台可形成首尾反转,属于项目自生成音乐,仓库暂无成品音频。',cues:[{from:'00:00',to:'00:18',scene:'01',direction:'残缺礼乐主题压在低频中。'},{from:'00:18',to:'00:36',scene:'02',direction:'木门燃烧处加入短促噪点。'},{from:'00:36',to:'00:54',scene:'03',direction:'父子对峙处撤去主鼓。'},{from:'00:54',to:'01:12',scene:'04',direction:'符命主题只剩单音。'},{from:'01:12',to:'01:29',scene:'05',direction:'绶带易手后主旋律消失。'}]},
sources:[{claim:'长安四面会兵、宣平门入、未央宫火攻、王邑父子、王莽退渐台、杜吴取绶与新亡',citation:'《汉书·王莽传下》卷九十九下',url:'https://zh.wikisource.org/zh-hans/汉书/卷099下'}],
blacklist:['把攻入长安的各路汉兵画成东汉中央政府的统一正规军;此时仍属更始诸军及地方兵马','把渐台画成高山孤堡、佛塔或后世楼阁;它是未央宫池水中的台','把王莽所抱符命画成发光神书,或把威斗误作兵器','展示王莽被杀、斩首、分尸、悬首或群众食舌等血腥细节','把杜吴塑造成预先奉命斩王莽的著名将军;原文明确称商人','现代文字地图、水印、后世服饰建筑、火器爆炸或动漫画风']
}
]
function md(p){const a=[`# ${p.event.canonicalName}|素材说明`,'','## 事件边界','',`- 规范事件名:${p.event.canonicalName}`,`- 常用称呼:${p.event.aliases.join('、')}`,`- 时代:${p.event.era}`,`- 日期:${p.event.date}`,`- 地点:${p.event.place}`,`- 一句话结论:${p.event.summary}`,'','## 60–90 秒主旁白','',p.narration.fullText,'','## 分幕素材',''];for(const s of p.scenes)a.push(`### ${s.id}|${s.title}`,'',`- 时间地点:${s.timePlace}`,`- 本幕作用:${s.purpose}`,`- 分幕旁白:${s.narration}`,`- 页面标题:${s.overlay.heading}`,`- 页面短句:${s.overlay.line}`,`- PNG:\`${s.image.original}\``,`- WebP:\`${s.image.delivery}\``,`- 替代文本:${s.image.alt}`,`- 视觉推演边界:${s.image.visualInference}`,`- 人物:${s.people.map(x=>`${x.name}(${x.office};${x.action})`).join(';')}`,`- 史料:${s.sources.join(';')}`,'','生成提示词:','','```text',s.image.prompt,'```','');a.push('## BGM 选曲','',`- 项目曲目:\`${p.bgm.catalogId}\`|${p.bgm.title}|${p.bgm.version}`,`- 状态:${p.bgm.status}`,`- 用法:${p.bgm.usage}`,`- 选择理由:${p.bgm.reason}`,'- 音频路径或 URL:仓库暂缺,待导出','','| 时间 | 分幕 | 编排 |','|---|---|---|',...p.bgm.cues.map(x=>`| ${x.from}–${x.to} | ${x.scene} | ${x.direction} |`),'','## 史料来源','',...p.sources.map(x=>`- ${x.claim}:[${x.citation}](${x.url})`),'','## 禁区与误读防护','',...p.blacklist.map(x=>`- ${x}`),'','## 交付检查','','- [ ] 每幕 PNG 原图存在且为 16:9','- [ ] 每幕 WebP 交付图存在且为 16:9','- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全','- [ ] BGM 已导出并填写可用路径','- [ ] \`manifest.json\` 与本说明中的文件名全部校验通过','');return a.join('\n')+'\n'}
for(const p of packs){const d=path.join(outRoot,p.dir);if(fs.existsSync(d))throw new Error(`Refusing to overwrite ${d}`);fs.mkdirSync(path.join(d,'images'),{recursive:true});fs.writeFileSync(path.join(d,'manifest.json'),JSON.stringify({event:p.event,narration:p.narration,scenes:p.scenes,bgm:p.bgm,sources:p.sources,blacklist:p.blacklist},null,2)+'\n');fs.writeFileSync(path.join(d,'素材说明.md'),md(p))}
console.log(`Generated ${packs.length} Xin-dynasty gap packages`)
@@ -0,0 +1,111 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outRoot = path.join(root, '战役素材')
const BASE = 'Premium live-action historical film still, archaeology-informed and specific to the stated century and place, cinematic 16:9 landscape, subtle film grain, physically plausible people, terrain, architecture, ships, weapons, clothing and logistics. Leave calm negative space for webpage copy. No baked-in text, readable banners, subtitles, logo or watermark; no clone-face armies, fantasy armor, imperial yellow dragon spectacle, Forbidden City unless explicitly required, magic, anime, gore or body piles.'
const image = (file, request, inference, refinementPrompt = '') => ({ original: `images/${file}.png`, delivery: `images/${file}.webp`, alt: request, prompt: `Use case: historical-scene. Digital Me Yuan, Ming and Qing story frame. ${request} ${BASE}`, visualInference: inference, refinementPrompt })
const person = (name, office, action) => ({ name, office, action })
const cue = (scene, from, to, direction) => ({ scene, from, to, direction })
const packs = [
{
dir: '元/弘安征日本(江南军覆师)',
event: { canonicalName: '弘安征日本(江南军覆师)', aliases: ['弘安之役', '元日战争第二次远征', '第二次征日本'], projectDynasty: '元', era: '元世祖至元十八年、日本弘安四年', date: '公元1281年', place: '高丽合浦、博多湾至壹岐与鹰岛附近海域', summary: '元朝组织东路军与江南军跨海征日本,两支舰队会合和补给失调,在日本沿岸作战未能建立稳定基地;舰队停泊期间又遭强烈风暴,船只大量毁损,远征以惨败告终。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '至元十八年,忽必烈发动第二次征日本。远征军被分成两路:东路军从高丽出发,江南军则由范文虎等率领,从庆元、定海一带渡海。计划要求两军会合后共同进攻,但船队规模过大、出发时间不同,粮水与指挥难以统一。东路军先到博多湾,面对日本军队的岸防和反复袭扰,不能像在大陆战场那样迅速展开骑兵。江南军后来抵达,两路舰船在壹岐、鹰岛附近聚集,却仍没有建立足够稳固的登陆场和补给体系。八月,强烈风暴袭击停泊海面的舰队。许多为赶工征集的船只互相碰撞、断缆搁浅,大量士卒失散。范文虎等乘较坚固的船先行撤离,留在岛上的军队或被俘,或无法归国。元军第二次征日由此失败。风暴是毁灭性一击,却不是唯一原因;跨海调度、船只质量、会师延误、岸防压力与补给困难,早已让这支庞大舰队暴露在风险之中。' },
scenes: [
{ id: '01', title: '两路渡海·会师失期', timePlace: '高丽合浦与江南庆元船队出航后', purpose: '说明两支舰队来源、时间和后勤不同,避免把远征简化成一夜风暴。', narration: '东路军与江南军分别渡海。船型、航期和指挥系统不一,使原定会师与补给从一开始就很脆弱。', overlay: { heading: '十万军队,分乘上千条船', line: '跨海以前,最难统一的不是战旗,而是出发时间、粮水和每一艘船的状态。' }, people: [person('忽必烈', '元朝皇帝', '下令两路大军跨海征日;不出现在海上'), person('范文虎', '江南军主将', '率江南军船队渡海'), person('忻都、洪茶丘', '东路军将领', '由高丽方向先行出航'), person('船工与军户', '水手、征发工匠与士卒', '操船、修缆并管理粮水')], image: image('01-two-yuan-fleets-struggle-to-rendezvous', 'Summer 1281 at sea between the Korean peninsula and northern Kyushu. A mixed Yuan expedition fleet of practical thirteenth-century transports, horse carriers and command craft sails in separated groups. In the foreground Jiangnan commander Fan Wenhu reviews blank navigation tallies beside tired Chinese and Korean sailors repairing ropes and water casks; the earlier eastern-route fleet is only a distant line on the horizon. No storm yet, no modern junk fleet, no treasure ships, no Japanese torii gate.', '两支船队距离、船型差异和甲板补给为视觉重构;分路出航、会师失期与江南军后至有史载。'), sources: ['《元史·日本传》卷二百八', '《元史·世祖本纪》卷十一'] },
{ id: '02', title: '沿岸受阻·舰队滞留', timePlace: '博多湾、壹岐至鹰岛一带', purpose: '呈现岸防、短促接战和登陆场不足造成的滞留。', narration: '元军抵达日本沿岸,却无法建立稳定基地。日本武士依托岸防反复出击,舰队被迫长期停泊。', overlay: { heading: '海上没有草原纵深', line: '马匹、粮袋和数万士卒都挤在船上,任何一次会师迟延都会变成补给压力。' }, people: [person('范文虎', '江南军主将', '等待会师并寻找登陆时机'), person('元军水陆部队', '汉军、蒙古军、高丽军与水手', '在船上维持战备和补给'), person('日本镰仓幕府军', '九州御家人与沿岸守军', '依托石垒与小艇袭扰舰队')], image: image('02-yuan-fleet-stalled-off-kyushu', 'Off northern Kyushu in 1281, crowded Yuan transports anchor beyond a low coastal stone defense. Small Japanese craft probe the outer ships while armored defenders remain behind the shore wall; aboard the fleet, handlers calm horses and sailors ration water. Show tense stalemate and an insecure landing zone, not a decisive samurai charge, giant castle, modern cannon, fantasy fire arrows or storm.', '石垒、锚泊密度、马匹和取水压力为视觉转译;元军沿岸作战受阻、舰队在岛屿附近滞留有史载。'), sources: ['《元史·日本传》卷二百八', '《高丽史·忠烈王世家》'] },
{ id: '03', title: '暴风破舟·远征撤退', timePlace: '鹰岛附近锚地与外海', purpose: '克制呈现风暴毁舰和弃军撤退,不把自然灾害神化。', narration: '强风袭击锚地,舰船断缆、碰撞和搁浅。主将乘坚船撤离,失散军队无力继续作战。', overlay: { heading: '风暴击中了已经过载的舰队', line: '船破、粮尽、指挥失联之后,远征不再是如何取胜,而是谁还能回到大陆。' }, people: [person('范文虎', '江南军主将', '选择坚船撤离,后受追责'), person('元军水手与士卒', '远征军基层人员', '在风暴后修船、集结与等待撤运'), person('日本沿岸军', '岛上守军', '在风暴后控制登陆残军')], image: image('03-storm-shatters-yuan-fleet-near-takashima', 'A violent night storm near Takashima breaks the overloaded thirteenth-century fleet: several wooden ships drag anchors and collide, one grounds on rocks, masts and ropes fail. At first light, exhausted survivors gather on a beach around intact water jars while a few sturdy command ships recede offshore. No divine figures, radiant wind, ship explosions, drowning close-ups, corpses or triumphant samurai posing.', '具体船损、幸存者集合与坚船离岸为视觉推演;暴风破舟、诸将择坚舰撤离及远征覆败有史载。'), sources: ['《元史·日本传》卷二百八', '《元史·世祖本纪》卷十一'] },
],
bgm: { catalogId: 'mingchangmian:hongan_zhengri#2', title: '弘安征日本·十万之众得还者三人', version: '名场面专属·器乐与风暴 stem', status: 'pending-export', pathOrUrl: '', usage: '会师失期用稀疏船木和低弦,沿岸滞留增加压迫脉冲,风暴层层堆至桅断后骤静;删除夸张数字唱词、人声和品牌尾签。', reason: '项目已有弘安征日本专属名场面配乐,结构覆盖集结、滞留和风暴覆师;仓库暂无成品音频。', cues: [cue('01','00:00','00:28','分散节拍表现两路船队失期。'), cue('02','00:28','00:55','低弦与船木声形成停滞压力。'), cue('03','00:55','01:26','风暴升至断裂后骤静收尾。')] },
sources: [{ claim: '江南军规模、暴风破舟与诸将先遁', citation: '《元史·日本传》卷二百八', url: 'https://zh.wikisource.org/zh-hans/元史/卷208' }, { claim: '至元十八年征日本的朝廷纪年', citation: '《元史·世祖本纪》卷十一', url: 'https://zh.wikisource.org/zh-hans/元史/卷011' }],
blacklist: ['把战争解释为超自然“神风”单独决定', '把所有船只画成同一种明代宝船或现代帆船', '把蒙古骑兵直接画成在海面冲锋', '展示大规模溺亡、尸体或把灾难拍成浪漫奇观', '现代文字、水印、火炮舷窗、明清盔甲与日本近世城郭'],
},
{
dir: '明/土木堡之变(明英宗被俘)',
event: { canonicalName: '土木堡之变(明英宗被俘)', aliases: ['土木之变', '土木堡之败', '英宗北狩'], projectDynasty: '明', era: '明英宗正统十四年', date: '公元1449年八月', place: '大同、宣府至土木堡一线', summary: '明英宗在王振推动下率京军仓促亲征瓦剌,撤军途中因路线反复与补给困难停驻土木堡;也先军控制水源并完成包围,明军突围时崩溃,英宗被俘,京营主力与多名大臣损失惨重。' },
narration: { language: 'zh-CN', estimatedSeconds: 87, fullText: '正统十四年,瓦剌也先分路侵入明境。司礼太监王振主张皇帝亲征,明英宗在准备不足的情况下率京营和大量随从仓促出塞。庞大队伍既有作战军队,也有官员、民夫、辎重和仪仗,行军速度缓慢。抵近大同后,前线败报不断,朝廷决定撤军;路线却因王振等人的意见反复改变,使粮水消耗和队形混乱进一步加重。八月,明军停在土木堡。这里地势暴露,附近水源很快被瓦剌控制。也先先以议和和退兵迹象诱使明军移动,饥渴已久的队伍在取水、转营时失去阵形,瓦剌骑兵随即进攻。明军迅速崩溃,扈从公侯和部院大臣多人战死,英宗被俘。史书所谓“北狩”,是对天子成为俘虏的克制写法。土木堡之变不仅是一场野战失败,它使明朝最重要的京营遭到重创,也把没有皇帝、精锐尽失的北京交给留守群臣。接下来是南迁还是守城,成为另一个决定王朝命运的问题。' },
scenes: [
{ id: '01', title: '仓促亲征·大军折返', timePlace: '京师至大同、宣府道路', purpose: '建立御驾亲征规模与后勤失衡,而非把败因全归一人。', narration: '英宗率京营仓促出塞,前线不利后又折返。军民混杂的长队在反复改道中消耗粮水。', overlay: { heading: '皇帝出征不等于军队准备好了', line: '仪仗、官员和辎重把队伍拉得越来越长,命令却在撤退路线上来回改变。' }, people: [person('明英宗朱祁镇', '明朝皇帝', '亲征瓦剌并随军撤回'), person('王振', '司礼监太监', '推动亲征并干预行军路线'), person('邝埜', '兵部尚书', '随驾并多次提出军事意见'), person('京营军民', '军士、民夫与辎重人员', '在仓促动员中承担运输与护卫')], image: image('01-ming-imperial-column-retreats-from-datong', 'Northern China in August 1449. A vast but tired Ming column reverses course on the road from Datong: disciplined cavalry at the edges, dense carts, pack animals, civil officials and laborers stretching into dust. The young emperor rides under a restrained field canopy while Wang Zhen and military ministers argue over a blank route map. Show logistical strain, no battle, no Forbidden City procession, no Qing queue hairstyles.', '队伍构成、路线争议和辎重密度为视觉重构;亲征仓促、至大同后撤与道路反复有史载。'), sources: ['《明史·英宗前纪》卷十', '《明史·王振传》卷三百四'] },
{ id: '02', title: '停驻土木·水源被控', timePlace: '土木堡营地与附近河道', purpose: '呈现暴露营地、缺水与议和诱动导致的战术危机。', narration: '明军停在土木堡,也先军控制取水道路。议和消息传来后,饥渴部队开始移动,阵形随即松动。', overlay: { heading: '堡在这里,水却不在营里', line: '军阵还没有被冲破,争相取水和仓促移营已经让它从内部散开。' }, people: [person('也先', '瓦剌太师', '包围明军、控制水源并诱其移动'), person('明英宗', '亲征皇帝', '困于中军'), person('王振与随驾官员', '中枢随从', '在缺水和议和消息间作出混乱指令'), person('京营军士', '明军主力', '在饥渴中维持营阵')], image: image('02-oirat-encirclement-cuts-water-at-tumu', 'At exposed Tumu camp in late summer, Ming carts form an incomplete perimeter on dry ground while soldiers queue with empty leather water bags. Beyond them, Oirat cavalry controls the shallow river and encircles the camp from rolling hills. A messenger arrives with blank negotiation tablets as units begin shifting too early. No full battle yet, no desert dunes, no giant stone fortress, no cloned cavalry wall.', '营垒形态、水路位置和部队提前移动为视觉推演;瓦剌围营控水、议和诱动与明军失序有史载。', '重制时明确所有明军男性使用明代束发、巾帽或头盔,排除剃额蓄辫、清代官服与补子,并将普通军士和空水囊置于前景。'), sources: ['《明史·英宗前纪》卷十', '《明史·瓦剌传》卷三百二十八'] },
{ id: '03', title: '军阵崩溃·天子被俘', timePlace: '土木堡营外', purpose: '交代溃败和英宗被俘,同时把下一场北京守城留作后续。', narration: '明军移动时遭骑兵突击,营阵迅速崩溃。多名大臣战死,英宗放下武器后被瓦剌俘获。', overlay: { heading: '一天之内,京营与天子一同失去', line: '北京得到的不是凯旋消息,而是一支精锐尽失、皇帝在敌营的朝廷。' }, people: [person('明英宗', '明朝皇帝', '在乱军后被瓦剌俘获'), person('也先', '瓦剌太师', '取得大胜并控制英宗'), person('邝埜、王佐等', '随驾部院大臣', '土木之败中战死;不展示遗体'), person('明军残部', '京营将士与民夫', '分散突围返回关内')], image: image('03-ming-army-collapses-and-emperor-is-captured', 'After the collapse outside Tumu, scattered Ming survivors move toward a distant pass while abandoned carts and furled standards mark the broken camp. In a quiet foreground, the young emperor stands exhausted and unarmed before Oirat officers, guarded but not humiliated; ministers are absent rather than shown dead. No kneeling spectacle, execution, corpses, heroic duel or Qing clothing.', '英宗被俘姿态、弃车与残部方向为克制视觉转译;明军溃败、扈从重臣死与天子被俘有史载。', '重制时指定英宗为二十二岁男性、明代束发与朴素红色野战袍,站立受控但不跪地、不羞辱,并排除女性、儿童和清代服饰。'), sources: ['《明史·英宗前纪》卷十', '《明史·七卿年表》卷一百十一'] },
],
bgm: { catalogId: 'zhanzhen:tumu#0', title: '土木堡之变', version: '战役专属·电影级器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '撤军用沉重脚步和车轮,断水段逐步抽掉低频,合围只形成一次短促冲击,英宗被俘后骤静;删除人声、具体夸张伤亡唱词和品牌尾签。', reason: '项目已有土木堡之变专属曲目,行军、合围、群体崩溃与冷反转结构一致;仓库暂无成品音频。', cues: [cue('01','00:00','00:29','车轮和脚步建立过载撤军。'), cue('02','00:29','00:57','水源被控后节奏越来越空。'), cue('03','00:57','01:27','合围峰值后落为单人冷景。')] },
sources: [{ claim: '土木被围、军溃与英宗北狩', citation: '《明史·英宗前纪》卷十', url: 'https://zh.wikisource.org/zh-hans/明史/卷010' }, { claim: '王振推动亲征及行军干预', citation: '《明史·宦官传》卷三百四', url: 'https://zh.wikisource.org/zh-hans/明史/卷304' }, { claim: '瓦剌与也先用兵经过', citation: '《明史·瓦剌传》卷三百二十八', url: 'https://zh.wikisource.org/zh-hans/明史/卷328' }],
blacklist: ['把数十万伤亡画成整齐尸山或用夸张数字作画面事实', '把败因只归结为王振个人而忽略动员、路线、水源和指挥体系', '把土木堡画成巨大坚固城池', '羞辱化展示被俘皇帝或直接展示大臣死亡', '现代文字、水印、清代辫发、清式服甲、现代火器与奇幻骑兵'],
},
{
dir: '明/北京保卫战(于谦守九门)',
event: { canonicalName: '北京保卫战(于谦守九门)', aliases: ['北京保卫战', '京师保卫战', '于谦守北京'], projectDynasty: '明', era: '明代宗景泰元年', date: '公元1449年九月至十月', place: '北京九门及德胜门、彰义门一带', summary: '土木堡惨败后,也先挟被俘英宗南下,明廷拥立景帝并拒绝南迁;于谦整编京军,将部队布置于北京城外各门,明军在德胜门等处击退瓦剌进攻,守住京师。' },
narration: { language: 'zh-CN', estimatedSeconds: 86, fullText: '土木堡败报传到北京,皇帝被俘,京营主力损失殆尽。朝廷有人主张迁都南京,兵部侍郎于谦则坚持京师是天下根本,一旦南迁,人心与北方防线都会瓦解。群臣拥立郕王朱祁钰为帝,以稳定名分;于谦升任兵部尚书,调集两京备操军、地方勤王军和残余京军,重新编组成能够守城的部队。他没有只把人藏在城墙后,而是把主力列在九门之外,背城作战,并宣布临阵退却者斩。十月,也先挟英宗抵近北京,希望借天子身份迫使开门。明廷拒绝把社稷系于一人。德胜门方向,明军先以少量骑兵诱敌接近,再用火器、弓弩和伏兵反击;彰义门等处也发生激战。瓦剌无法迅速攻破京师,又担心各地援军合围,最终撤退。北京保卫战不是于谦一个人站在城头喝退敌军,而是政治上另立皇帝、军事上重新动员、城外列阵和各门协同共同完成的危机应对。' },
scenes: [
{ id: '01', title: '拒绝南迁·重建指挥', timePlace: '北京宫城外廷与兵部', purpose: '交代拥立景帝、拒迁和重新动员是守城前提。', narration: '于谦力主固守,明廷拥立景帝稳定朝局,并调集京内外可用兵力重编九门防务。', overlay: { heading: '皇帝丢了,城不能再丢', line: '守北京的第一步不是筑墙,而是让朝廷仍能下达一套所有人都承认的命令。' }, people: [person('于谦', '兵部侍郎、后任兵部尚书', '反对南迁并主持京师防务'), person('朱祁钰', '郕王、后即明代宗', '在危局中即位稳定朝廷'), person('孙太后与群臣', '皇室和中枢官员', '决定监国、即位与固守'), person('京军与勤王军', '守城各部', '重新登记、领械和分派城门')], image: image('01-yu-qian-rebuilds-beijing-defense-command', 'Inside a restrained Ming government courtyard in 1449, Yu Qian and officers assign blank wooden rosters to commanders from several differently equipped units. Armor, spears, early handguns and supply carts are being inventoried; the palace roofs remain distant and secondary. Show emergency mobilization and a new chain of command, not a throne-room pageant, no readable edict, no battle yet.', '军册分发、兵器清点和各部混编为视觉重构;拒绝南迁、拥立景帝与于谦统筹守城有史载。'), sources: ['《明史·于谦传》卷一百七十', '《明史·代宗本纪》卷十一'] },
{ id: '02', title: '背城列阵·德胜门伏击', timePlace: '北京德胜门外', purpose: '呈现主力列在城外和诱敌伏击,不画成孤城远程轰炸。', narration: '于谦把各军列在九门外。德胜门明军诱使瓦剌骑兵接近,以火器、弓弩和伏兵协同反击。', overlay: { heading: '城门在身后,退路也在身后', line: '把兵摆到墙外,是要告诉新拼起来的军队:这一阵只能向前。' }, people: [person('于谦', '兵部尚书', '总督军务并巡督各门'), person('石亨', '武清伯、守将', '在德胜门方向统兵作战'), person('也先', '瓦剌太师', '率军逼近北京并寻找破口'), person('明军各部', '火器手、弩手、骑步军', '在城门外分层布阵')], image: image('02-ming-ambush-halts-oirat-at-deshengmen', 'Outside Deshengmen in autumn 1449, Ming troops form layered ranks with pavises, bows, early handguns and a few small period cannon; a limited cavalry screen falls back toward concealed infantry as Oirat horsemen approach across open ground. The brick city wall anchors the rear. Show coordinated defense with smoke kept low, no gigantic explosions, machine-gun volleys, Qing cannon crews or soldiers standing only atop walls.', '伏兵位置、兵种层次和烟雾尺度为视觉推演;德胜门外列阵、诱敌与火器反击有史载。'), sources: ['《明史·于谦传》卷一百七十', '《明史·石亨传》卷一百七十三'] },
{ id: '03', title: '挟帝叫门·瓦剌撤围', timePlace: '北京城外与撤军道路', purpose: '交代朝廷拒绝以天子换城及瓦剌撤退的结果。', narration: '也先挟英宗到城下交涉,明廷拒绝开门。攻城受挫且勤王军渐近,瓦剌最终北撤。', overlay: { heading: '社稷不再系于一人', line: '城门没有因被俘天子而打开,也先手中最重要的筹码因此失效。' }, people: [person('明英宗', '被俘太上皇', '被瓦剌带至城外作为谈判筹码'), person('也先', '瓦剌太师', '叫门不成后撤军'), person('于谦与守城官军', '京师防务中枢', '拒绝开门并维持追守'), person('勤王军', '各地援军', '逐步接近京师形成外部压力')], image: image('03-oirat-withdraws-after-beijing-holds', 'Cold dawn outside a closed Ming city gate. The captive former emperor remains on horseback beside Oirat envoys at a respectful distance while defenders watch from wall positions without opening. Farther away, Oirat wagons and cavalry begin an orderly northward withdrawal as Ming relief standards approach on another road. No humiliation, kneeling emperor, celebratory parade, readable banners or immediate reunion.', '城门交涉、撤军与援军同框为时序压缩;也先挟英宗叫门、攻城不克并北撤有史载。'), sources: ['《明史·于谦传》卷一百七十', '《明史·英宗前纪》卷十'] },
],
bgm: { catalogId: 'zhanzhen:beijingbaoweizhan#2', title: '北京保卫战', version: '战役专属·老腔与器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '重建指挥以低鼓推进,德胜门战斗让老腔与鼓点形成唯一峰值,叫门失败后落回城门低响;删除歌词、人声口号和品牌尾签。', reason: '项目已有北京保卫战专属曲目,拒迁、九门协同与“社稷重于一人”的结构完整对应;仓库暂无成品音频。', cues: [cue('01','00:00','00:28','名册、脚步与低鼓重建秩序。'), cue('02','00:28','00:57','德胜门伏击形成战斗峰值。'), cue('03','00:57','01:26','关门静止后转入北撤节奏。')] },
sources: [{ claim: '于谦拒迁、整军列阵九门与击退也先', citation: '《明史·于谦传》卷一百七十', url: 'https://zh.wikisource.org/zh-hans/明史/卷170' }, { claim: '景帝即位和京师防务的朝廷纪年', citation: '《明史·代宗本纪》卷十一', url: 'https://zh.wikisource.org/zh-hans/明史/卷011' }, { claim: '英宗被挟至京与瓦剌退兵', citation: '《明史·英宗前纪》卷十', url: 'https://zh.wikisource.org/zh-hans/明史/卷010' }],
blacklist: ['把北京保卫战画成于谦单人站城头喝退瓦剌', '把所有战斗放在城墙顶而忽略九门外列阵', '把火器画成近代步枪、机关枪或清末火炮', '羞辱化展示被俘英宗或编造城门打开', '现代文字、水印、清代辫发与故宫今日旅游景观'],
},
{
dir: '清/山海关之战(一片石决战)',
event: { canonicalName: '山海关之战(一片石决战)', aliases: ['山海关之战', '一片石之战', '吴三桂开关'], projectDynasty: '清', era: '清世祖顺治元年、明崇祯十七年', date: '公元1644年四月', place: '山海关、石河与一片石一带', summary: '李自成率大顺军东征山海关,吴三桂在兵力压力下向清摄政王多尔衮求援;吴军与大顺军激战后,清军骑兵投入战场击退李自成,清廷由此取得入关并进占北京的通道。' },
narration: { language: 'zh-CN', estimatedSeconds: 87, fullText: '崇祯十七年三月,李自成攻入北京,明朝灭亡。驻守山海关的吴三桂夹在三股力量之间:北京已被大顺控制,关外是多尔衮率领的清军,关内还有他自己的军队和家属。李自成希望取得山海关,亲率大军东进。吴三桂先与大顺接触,随后转而向多尔衮求援。多尔衮没有立即把清军全部交给吴三桂指挥,而是要求其归附,并在关外等待两军消耗。四月二十一日至二十二日,大顺军与吴军在石河、一片石一带激战。吴军伤亡增加、阵线吃紧后,多尔衮命清军骑兵从侧翼投入;清军要求吴军以白布作识别,避免混战。大顺军无法同时承受两面压力,开始向西撤退。李自成回到北京后很快离开,清军则通过山海关进入关内,随后占领北京。后世常把这场战争归结为“冲冠一怒为红颜”,但陈圆圆的故事不能替代真实的政治和军事选择:降清求援、关口归属和三方力量消长,才是改变天下走向的核心。' },
scenes: [
{ id: '01', title: '大顺东进·吴军求援', timePlace: '山海关内外与石河道路', purpose: '建立三方势力关系和吴三桂求援的政治条件。', narration: '李自成率大顺军逼近山海关。吴三桂转向关外多尔衮求援,并以归附换取清军介入。', overlay: { heading: '关门两侧都是新的天下', line: '吴三桂不是在明与清之间选择,而是在已经进入北京的大顺与尚在关外的清军之间押注。' }, people: [person('吴三桂', '原明辽东总兵', '守山海关、向清求援并归附'), person('李自成', '大顺皇帝', '率军东征争夺山海关'), person('多尔衮', '清摄政王', '率军至关外并提出归附条件'), person('关宁军', '吴三桂所属边军', '修补关防并准备野战')], image: image('01-three-forces-converge-at-shanhaiguan', 'Spring 1644 around Shanhaiguan. The pass and Great Wall sit between sea and mountains. Inside the gate, Wu Sangui confers with exhausted frontier officers over a blank map as Shun columns approach along the western road; beyond the eastern side, Dorgon’s Manchu cavalry camps at a deliberate distance. Use terrain to show three forces without split-screen magic, no Chen Yuanyuan portrait, no open gate yet.', '三方位置、军营距离和地图会议为视觉重构;大顺东征、吴三桂求援与多尔衮至关外有史载。'), sources: ['《明史·李自成传》卷三百九', '《清史稿·世祖本纪》卷四'] },
{ id: '02', title: '石河鏖战·清骑投入', timePlace: '山海关外石河与一片石战场', purpose: '呈现吴军先战、清军后从侧翼加入的决战顺序。', narration: '吴军先与大顺军鏖战,阵线吃紧。多尔衮识别吴军后投入骑兵,从侧翼冲击大顺军。', overlay: { heading: '关门打开,不等于胜负已经决定', line: '真正改变战局的是清军选择投入的时间,以及大顺军突然面对的第二个正面。' }, people: [person('吴三桂', '关宁军统帅', '率军先与大顺军作战'), person('多尔衮', '清摄政王', '等待战机后命清军投入'), person('李自成', '大顺军统帅', '督军争夺关口'), person('关宁军与清军', '边军和八旗骑兵', '以白布识别后协同作战')], image: image('02-qing-cavalry-enters-yipianshi-battle', 'On the windy Yipianshi battlefield, exhausted Wu frontier infantry and cavalry marked with simple white cloth strips hold a wavering line against Shun troops. From the coastal flank, organized Manchu cavalry enters under Dorgon’s command, still separated enough to read the sequence. Dust and a sudden weather shift add confusion without supernatural storm. No cloned cavalry flood, corpses, fantasy armor or single heroic duel.', '白布识别、清军投入角度和天气变化为视觉推演;吴军先战、清军随后突击、大顺军受两面压力有史载。'), sources: ['《清史稿·睿忠亲王多尔衮传》卷二百十八', '《明史·李自成传》卷三百九'] },
{ id: '03', title: '大顺西撤·清军入关', timePlace: '山海关西门与通往北京道路', purpose: '交代战役结果和清军入关,但不把北京政权更替压成同一天。', narration: '大顺军战败西撤,李自成放弃北京。清军经山海关进入关内,获得通向京师的道路。', overlay: { heading: '一片石之后,关门的方向变了', line: '清军不再只是关外援兵,而成为沿着驿道进入北京的新统治力量。' }, people: [person('李自成', '大顺皇帝', '率败军西撤并随后离京'), person('多尔衮', '清摄政王', '指挥清军入关并进向北京'), person('吴三桂', '平西王前身、降清将领', '随清军追击大顺'), person('山海关军民', '守军与沿路百姓', '见证军队换旗通行;旗面无字')], image: image('03-shun-retreats-and-qing-army-enters-pass', 'After battle, Shun troops withdraw west along the imperial road in dusty but coherent groups while damaged carts are collected. At Shanhaiguan, Manchu cavalry and Wu’s frontier units pass through the gate under controlled supervision, with civilians watching from a distance. Show a strategic transition, not immediate coronation in Beijing, looting, massacre, readable flags or a triumphant Chen Yuanyuan scene.', '撤军秩序、两军过关和百姓旁观为视觉转译;大顺败退、清军入关并进占北京有史载。'), sources: ['《清史稿·世祖本纪》卷四', '《清史稿·睿忠亲王多尔衮传》卷二百十八'] },
],
bgm: { catalogId: 'zhanzhen:shanhaiguanzhizhan#0', title: '山海关之战', version: '战役专属·电影级器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '三方对峙以低频拉开距离,一片石投入清骑形成唯一爆点,入关段不作凯旋而转为沉重行军;删除歌词、人物独白和品牌尾签。', reason: '项目已有山海关之战专属曲目,关门、三方决战和身份转折与本包对应;仓库暂无成品音频。', cues: [cue('01','00:00','00:29','三方军营以不相交节奏对峙。'), cue('02','00:29','00:59','清骑投入形成全片唯一峰值。'), cue('03','00:59','01:27','战鼓转为沉重入关脚步。')] },
sources: [{ claim: '李自成东征与山海关战败', citation: '《明史·李自成传》卷三百九', url: 'https://zh.wikisource.org/zh-hans/明史/卷309' }, { claim: '多尔衮受降、入关与战役经过', citation: '《清史稿·睿忠亲王多尔衮传》卷二百十八', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷218' }, { claim: '清军入关及进占北京的朝廷纪年', citation: '《清史稿·世祖本纪》卷四', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷004' }],
blacklist: ['用“冲冠一怒为红颜”替代三方政治军事关系', '把清军画成开战即在吴军阵中或让多尔衮受其指挥', '把一片石画成孤立巨石决斗场', '把战后入关与北京即位压成同一画面', '现代文字、水印、近代枪炮、夸张辫发、尸山与屠杀'],
},
{
dir: '清/雅克萨之战(两次围城至议和)',
event: { canonicalName: '雅克萨之战(两次围城至议和)', aliases: ['雅克萨之战', '清俄雅克萨战争', '逐罗刹'], projectDynasty: '清', era: '清圣祖康熙二十四年至二十八年', date: '公元1685年至1689年', place: '黑龙江上游雅克萨城及尼布楚方向', summary: '清军在黑龙江流域组织水陆运输,于1685年攻取俄国哥萨克占据的雅克萨;俄军重返后,清军次年再次围城并截断援路,双方停战谈判,最终签订尼布楚条约,确定东段边界框架。' },
narration: { language: 'zh-CN', estimatedSeconds: 88, fullText: '十七世纪中后期,俄国哥萨克沿西伯利亚河道向黑龙江流域扩张,在雅克萨修筑木堡,并与当地索伦、达斡尔等居民发生冲突。康熙朝先多次交涉,又在黑龙江流域修船、设驿和储粮,为远距离用兵准备运输。康熙二十四年,彭春等率清军水陆并进,包围雅克萨,以火炮和招降迫使守军撤出。清军毁坏堡垒后撤,但俄军很快返回并加强工事。次年,萨布素主持第二次围城。清军修筑围垒、控制黑龙江水路,阻止援军和补给进入;守军因长期围困与疾病大幅减员。清廷收到俄方谈判请求后下令停止强攻,并允许向城内提供部分医疗帮助。此后两国使团在尼布楚谈判,1689年签订条约。雅克萨之战不是“藤牌一冲就破火枪”的传奇对决,而是一场依赖船队、粮站、火炮、围城线和外交信息的边疆战争。战争的终点也不是占领一座木堡,而是把暂时的军事优势转化为双方承认的边界安排。' },
scenes: [
{ id: '01', title: '水陆备运·初围雅克萨', timePlace: '黑龙江水道与雅克萨木堡', purpose: '呈现远距离运输和第一次攻城、撤军的阶段。', narration: '清军沿黑龙江组织粮船与陆军,于1685年包围雅克萨,以炮击和劝降迫使守军撤出。', overlay: { heading: '先修的是航道和粮站', line: '在最北方围一座城,炮弹到达以前,粮船、驿站和翻译必须先到。' }, people: [person('康熙帝', '清朝皇帝', '部署交涉与进兵;不亲临前线'), person('彭春', '都统、首次进攻主将', '率清军水陆围攻雅克萨'), person('萨布素', '黑龙江将军', '负责边地军政与后勤'), person('雅克萨俄军', '哥萨克守军', '守木堡并在议降后撤出')], image: image('01-qing-river-force-besieges-albazin', 'Summer 1685 on the upper Amur. Qing grain boats and shallow war craft unload cannon, fascines and provisions below the wooden Albazin fort while infantry, Manchu officers, Han gunners and local guides establish a measured siege line. Smoke comes from a few period cannon, not a continuous inferno. Russian Cossack defenders watch from log ramparts. No European stone castle, modern rifles, giant palace tent or ice.', '船队构成、炮位和多族群协作为视觉重构;清军水陆进至雅克萨、炮击劝降和守军撤出有史载。'), sources: ['《清史稿·圣祖本纪》卷七', '《清史稿·彭春传》卷二百五十二'] },
{ id: '02', title: '俄军复据·长围断援', timePlace: '雅克萨围城线与黑龙江江面', purpose: '区分第二次围城,突出围垒、水路与疾病而非神奇近战。', narration: '俄军重返并加固木堡后,萨布素再次围城。清军控制江面和援路,长期消耗守军。', overlay: { heading: '第二次不是攻进去,而是围下去', line: '木墙可以重建,外援、粮食和健康却会在每一天的封锁中减少。' }, people: [person('萨布素', '黑龙江将军', '主持第二次围城并截援'), person('阿列克谢·托尔布津', '俄军守将', '率守军固守,围城中受伤身亡;不展示死亡'), person('林兴珠与藤牌兵', '清军水战部队', '在江面阻击俄方援船'), person('清俄士卒', '围城双方基层人员', '修垒、巡河并承受严寒疾病')], image: image('02-long-winter-siege-cuts-albazin-supply', 'Early winter 1686 at Albazin. The rebuilt Russian log fort sits inside layered Qing earthworks rather than under a frontal charge. On the partly icy Amur, shielded Qing river troops intercept a small supply boat while sentries maintain the siege road; inside, Cossacks ration sacks and firewood. Show distance, cold and attrition, no magical wicker shields stopping bullets at point blank, no mass assault, corpses or modern fortifications.', '冰期程度、围垒层次和江面截援为视觉推演;俄军复据、清军再围并遏援有史载。'), sources: ['《清史稿·圣祖本纪》卷七', '《清史稿·萨布素传》卷二百八十'] },
{ id: '03', title: '奉诏停攻·谈判定界', timePlace: '雅克萨营地与尼布楚谈判前夕', purpose: '交代停战、救治和条约衔接,避免画成战场直接签约。', narration: '俄方请求谈判后,清廷命前线停止强攻。围城解除,双方使团随后在尼布楚议定边界。', overlay: { heading: '打这一仗,是为了让下一步能写进条约', line: '撤掉攻城炮并不等于放弃,而是把战场的优势转交给使团和地图。' }, people: [person('康熙帝', '清朝皇帝', '接受议和请求并下令缓攻'), person('萨布素', '黑龙江将军', '执行停攻、保留封锁并组织交接'), person('索额图', '清廷谈判代表', '后赴尼布楚谈判'), person('清俄军医与译员', '停战联络人员', '交换通知并照料病员')], image: image('03-albazin-ceasefire-leads-to-border-talks', 'At the thawing Albazin siege camp, Qing officers seal cannon vents and receive Russian envoys beside an unmarked boundary map; interpreters and medics carry plain supply boxes toward the fort under guard. In the distance, boats are prepared for later travel toward Nerchinsk, but no treaty is signed on the battlefield. No handshake spectacle, readable map labels, modern national flags or instant friendship tableau.', '封炮、医药交接和远行船只为议和视觉转译;清廷停攻、俄使请和及后续尼布楚谈判有史载。'), sources: ['《清史稿·圣祖本纪》卷七', '《清史稿·索额图传》卷二百六十九'] },
],
bgm: { catalogId: 'zhanzhen:yakesa#0', title: '雅克萨之战', version: '战役专属·电影级器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '水路准备以冷弦和桨声推进,第二次围城保持低压不爆燃,停攻后转为地图与呼吸感收束;删除歌词、人声和品牌尾签。', reason: '项目已有雅克萨之战专属曲目,强调“打是为了形成边界安排”的方向与本包一致;仓库暂无成品音频。', cues: [cue('01','00:00','00:29','桨声、卸载与炮位建立行动线。'), cue('02','00:29','00:59','冷弦持续压迫,不做英雄冲锋。'), cue('03','00:59','01:28','封炮后转为空间感和谈判动机。')] },
sources: [{ claim: '两次进讨雅克萨、围城遏援与奉诏缓攻', citation: '《清史稿·圣祖本纪》卷七', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷007' }, { claim: '首次进兵雅克萨的前线经过', citation: '《清史稿·彭春传》卷二百五十二', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷252' }, { claim: '尼布楚谈判与定界', citation: '《清史稿·索额图传》卷二百六十九', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷269' }],
blacklist: ['把雅克萨压缩成藤牌兵无敌于火枪的一次冲锋', '把俄军木堡画成欧洲大型石城或拿破仑时代要塞', '把1685与1686两次围城混成同一天', '把停战直接画成战场签订现代国界文件', '现代文字、水印、现代国旗、后装步枪、尸堆与民族羞辱'],
},
{
dir: '清/黄海海战(大东沟海战)',
event: { canonicalName: '黄海海战(大东沟海战)', aliases: ['黄海海战', '大东沟海战', '甲午黄海海战'], projectDynasty: '清', era: '清德宗光绪二十年', date: '公元1894年9月17日', place: '鸭绿江口大东沟外黄海海域', summary: '北洋水师完成护送运兵船任务后,在大东沟外与日本联合舰队遭遇并鏖战;北洋舰队多舰沉没或重创,致远舰管带邓世昌在舰伤严重时试图冲撞敌舰,致远随后沉没,双方当日脱离战场,但制海权逐步倒向日本。' },
narration: { language: 'zh-CN', estimatedSeconds: 89, fullText: '光绪二十年九月十七日,北洋水师在鸭绿江口完成护送陆军登陆后,于大东沟外海发现日本联合舰队。北洋舰队临战展开,主要以定远、镇远两艘铁甲舰为核心;日本舰队则以较快航速保持纵队机动。双方很快进入炮战。北洋舰艇装甲较厚,但速射炮、弹药供应、信号和编队训练存在差距;日本舰队利用速度集中攻击侧翼舰只。战斗持续数小时,超勇、扬威等舰先后退出或沉没,定远受创后仍维持指挥。致远舰伤势严重、弹药有限,管带邓世昌下令加速冲向日本舰队,史籍记载其意在撞击敌舰;致远随后中弹或中鱼雷沉没,邓世昌与多数官兵遇难。这里不应把“撞沉吉野”画成已经发生的事实,致远并未撞上目标。当日下午双方脱离接触。北洋水师保住部分主力舰,却损失多艘巡洋舰,此后行动更趋保守。黄海海战既有官兵死战,也暴露了一个近代海军体系在弹药、训练、维护和决策上的长期问题。' },
scenes: [
{ id: '01', title: '护航返航·两舰队遭遇', timePlace: '大东沟外黄海海面', purpose: '建立战前任务、舰型与队形差异,不复刻后世宣传画。', narration: '北洋水师完成护航后发现日本舰队。各舰从护航队形转入战斗,信号与位置调整十分仓促。', overlay: { heading: '海面上没有预先摆好的阵图', line: '运兵护航刚刚结束,两支舰队便在烟雾与距离判断中转入实战。' }, people: [person('丁汝昌', '北洋水师提督', '在定远舰统一指挥舰队'), person('刘步蟾', '定远舰管带', '执行旗舰航行与战斗指挥'), person('伊东祐亨', '日本联合舰队司令长官', '率舰队纵队接敌'), person('北洋水兵', '信号兵、轮机兵与炮手', '升旗传令、装填并维持航向')], image: image('01-beiyang-and-japanese-fleets-meet-at-dadonggou', 'Yellow Sea, morning of 17 September 1894. Historically accurate protected cruisers and two Chinese ironclads maneuver away from distant troop transports as signal flags rise without readable symbols. The Beiyang formation is broad and uneven; the Japanese Combined Fleet approaches in a disciplined steaming column. Coal smoke, steel and late-nineteenth-century rigging are accurate. No modern battleships, aircraft, missiles, wooden junks or pre-dreadnought fantasy turrets.', '舰队相对方向、烟迹和队形松紧为视觉重构;护航后遭遇、定远旗舰与日舰纵队机动有史载。'), sources: ['《清史稿·德宗本纪》卷二十三', '《清史稿·丁汝昌传》卷四百六十二'] },
{ id: '02', title: '鏖战数时·侧翼受创', timePlace: '大东沟主战场', purpose: '呈现速度、火力与装甲差异造成的战斗过程,避免满屏爆炸。', narration: '日舰利用航速集中攻击北洋侧翼,北洋舰队以重炮还击。多舰起火受损,旗舰仍在烟幕中维持作战。', overlay: { heading: '每艘军舰的速度都在改变整条战线', line: '装甲可以挡住一部分炮弹,却挡不住编队被拉开后出现的空隙。' }, people: [person('丁汝昌', '北洋水师提督', '负伤后仍维持旗舰指挥'), person('北洋各舰管带', '舰长与军官', '在信号受阻下各自应战'), person('日本联合舰队', '第一游击队与本队', '利用航速实施集中炮击'), person('两军水兵', '炮手、损管与轮机人员', '装填、灭火并抢修舰体')], image: image('02-cruiser-battle-and-damage-control-in-yellow-sea', 'Mid-battle at the Yalu in 1894. Fast Japanese cruisers turn across the far side while Beiyang warships answer with slower heavy guns. One Chinese cruiser lists with localized fire; in the foreground sailors pass hoses, buckets and shells behind steel splinter shields. Use realistic black-powder smoke and limited shell splashes, no city-sized fireballs, visible bodies, modern radar ships, torpedo trails everywhere or duplicated hulls.', '舰位、局部火灾与损管细节为视觉推演;日舰机动集火、北洋多舰受创和旗舰持续作战有史载。'), sources: ['《清史稿·德宗本纪》卷二十三', '《清史稿·丁汝昌传》卷四百六十二'] },
{ id: '03', title: '致远冲敌·舰队脱离', timePlace: '黄海战场傍晚', purpose: '克制呈现邓世昌的最后行动,并明确未撞上目标。', narration: '致远重创后加速冲向敌舰,未及撞击便沉没。傍晚双方脱离,北洋水师带伤舰返回。', overlay: { heading: '军舰成了最后一发炮弹', line: '意图撞击是最后的选择,不是已经撞沉敌舰的胜利结局。' }, people: [person('邓世昌', '致远舰管带', '下令冲向敌舰,随舰殉职'), person('致远舰官兵', '炮手、轮机兵与水手', '维持航速并执行最后机动'), person('北洋残存舰队', '定远、镇远等舰', '傍晚收拢伤舰退出战场'), person('日本舰队', '联合舰队各舰', '规避致远并继续炮击')], image: image('03-zhiyuan-charges-before-sinking-at-dusk', 'At dusk in the 1894 Yellow Sea battle, the damaged protected cruiser Zhiyuan drives through smoke toward a faster Japanese cruiser but remains clearly short of collision. Her bow is low, funnels damaged and deck crews still at stations; farther back, surviving Beiyang ships gather under smoke for withdrawal. Frame the final approach before sinking, no impact, no exploding enemy flagship, drowning close-ups, heroic halo or readable ship names.', '两舰未接触的距离、致远损伤与后方收拢为史实边界内的视觉转译;邓世昌冲敌、致远沉没及双方脱离有史载。', '重制时把致远置于左前景、舰艏明确朝右侧敌舰,要求两舰之间保留大片开阔水面,敌舰右转规避,从构图上排除已经相撞或撞沉敌舰。'), sources: ['《清史稿·邓世昌传》卷四百六十', '《清史稿·德宗本纪》卷二十三'] },
],
bgm: { catalogId: 'zhanzhen:zhengchenggong_taiwan#0', title: '郑成功收复台湾', version: '项目海战曲·晚清舰炮结构借用·仅器乐 stem', status: 'pending-export', pathOrUrl: '', usage: '仅借用海战推进、舰炮间歇和伤舰收束结构;删除郑成功、台湾、木帆船语义及全部人声,用更机械的低频和金属损管声重编,致远段不做胜利式爆点。', reason: '项目暂无黄海海战专曲;现有曲目中该曲最接近海上炮战的节奏结构,但两战时代、舰船与结果完全不同,必须重编。仓库暂无成品音频。', cues: [cue('01','00:00','00:29','蒸汽机低频与信号节奏建立遭遇。'), cue('02','00:29','00:59','炮战与损管形成持续而非爆燃的峰值。'), cue('03','00:59','01:29','冲敌不落胜利和弦,转为伤舰撤离。')] },
sources: [{ claim: '大东沟海战的朝廷纪年与北洋损失', citation: '《清史稿·德宗本纪》卷二十三', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷023' }, { claim: '邓世昌率致远冲敌与殉舰', citation: '《清史稿·邓世昌传》卷四百六十', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷460' }, { claim: '丁汝昌统领北洋水师及甲午战事', citation: '《清史稿·丁汝昌传》卷四百六十二', url: 'https://zh.wikisource.org/zh-hans/清史稿/卷462' }],
blacklist: ['把“撞沉吉野”画成已经发生;致远未撞上目标', '把甲午海战画成风帆木船、无畏舰、二战舰艇或现代导弹战', '用一场海战解释全部甲午败局而忽略体系问题', '展示舰员溺亡、尸体或把沉舰浪漫化', '现代文字、水印、错误国旗细节、超大爆炸与复制舰队'],
},
]
function markdown(pack) {
const lines = [`# ${pack.event.canonicalName}|素材说明`, '', '## 事件边界', '', `- 规范事件名:${pack.event.canonicalName}`, `- 常用称呼:${pack.event.aliases.join('、')}`, `- 项目归类:${pack.event.projectDynasty}`, `- 时代:${pack.event.era}`, `- 日期:${pack.event.date}`, `- 地点:${pack.event.place}`, `- 一句话结论:${pack.event.summary}`, '', '## 60–90 秒主旁白', '', pack.narration.fullText, '', '## 分幕素材', '']
for (const scene of pack.scenes) lines.push(`### ${scene.id}|${scene.title}`, '', `- 时间地点:${scene.timePlace}`, `- 本幕作用:${scene.purpose}`, `- 分幕旁白:${scene.narration}`, `- 页面标题:${scene.overlay.heading}`, `- 页面短句:${scene.overlay.line}`, `- PNG:\`${scene.image.original}\``, `- WebP:\`${scene.image.delivery}\``, `- 替代文本:${scene.image.alt}`, `- 视觉推演边界:${scene.image.visualInference}`, `- 人物:${scene.people.map((item) => `${item.name}(${item.office};${item.action})`).join(';')}`, `- 史料:${scene.sources.join(';')}`, '', '生成提示词:', '', '```text', scene.image.prompt, '```', '')
lines.push('## BGM 选曲', '', `- 项目曲目:\`${pack.bgm.catalogId}\`|${pack.bgm.title}|${pack.bgm.version}`, `- 状态:${pack.bgm.status}`, `- 用法:${pack.bgm.usage}`, `- 选择理由:${pack.bgm.reason}`, '- 音频路径或 URL:仓库暂缺,待导出', '', '| 时间 | 分幕 | 编排 |', '|---|---|---|', ...pack.bgm.cues.map((item) => `| ${item.from}–${item.to} | ${item.scene} | ${item.direction} |`), '', '## 史料来源', '', ...pack.sources.map((source) => `- ${source.claim}:[${source.citation}](${source.url})`), '', '## 禁区与误读防护', '', ...pack.blacklist.map((item) => `- ${item}`), '', '## 交付检查', '', '- [ ] 每幕 PNG 原图存在且为 16:9', '- [ ] 每幕 WebP 交付图存在且为 16:9', '- [x] 旁白、页面文案、人物标签、提示词、视觉推演和史料齐全', '- [ ] BGM 已导出并填写可用路径', '- [ ] `manifest.json` 与本说明中的文件名全部校验通过', '')
return `${lines.join('\n')}\n`
}
const refreshMetadata = process.argv.includes('--refresh-metadata')
for (const pack of packs) {
const directory = path.join(outRoot, pack.dir)
if (fs.existsSync(directory) && !refreshMetadata) throw new Error(`Refusing to overwrite ${directory}`)
fs.mkdirSync(path.join(directory, 'images'), { recursive: true })
fs.writeFileSync(path.join(directory, 'manifest.json'), `${JSON.stringify({ event: pack.event, narration: pack.narration, scenes: pack.scenes, bgm: pack.bgm, sources: pack.sources, blacklist: pack.blacklist }, null, 2)}\n`)
fs.writeFileSync(path.join(directory, '素材说明.md'), markdown(pack))
console.log(`${pack.dir}: ${pack.scenes.length} scenes, ${pack.narration.estimatedSeconds}s, ${pack.narration.fullText.length} chars`)
}
console.log(`Generated ${packs.length} Yuan, Ming and Qing core battle packages`)
+82
View File
@@ -0,0 +1,82 @@
from __future__ import annotations
import json
import zipfile
from pathlib import Path
from docx import Document
from docx.oxml.ns import qn
path = Path(r"D:\workspace\digital_me\artifacts\《我为你研墨》音频对齐脚本与分镜提示词.docx")
issues = []
with zipfile.ZipFile(path) as zf:
bad_member = zf.testzip()
if bad_member:
issues.append(f"Corrupt ZIP member: {bad_member}")
doc = Document(path)
all_text = "\n".join(p.text for p in doc.paragraphs)
for table in doc.tables:
for row in table.rows:
all_text += "\n" + "\t".join(cell.text for cell in row.cells)
for marker in ("TODO", "TBD", "【统一风格前缀】", "placeholder"):
if marker in all_text:
issues.append(f"Unexpected placeholder marker: {marker}")
scene_headings = [p.text for p in doc.paragraphs if p.style.name == "Heading 1" and p.text.startswith("镜 ")]
if len(scene_headings) != 15:
issues.append(f"Expected 15 scene headings, found {len(scene_headings)}")
if all_text.count("10秒图生视频提示词(可直接复制)") != 15:
issues.append("10-second prompt section count is not 15")
if all_text.count("生成时长严格10秒") != 15:
issues.append("Not every prompt is explicitly fixed to 10 seconds")
if all_text.count("统一负面提示|") != 15:
issues.append("Per-scene negative prompt count is not 15")
if "03:19.15" not in all_text or "199.152" not in all_text:
issues.append("Verified audio duration is missing")
table_geometry = []
for index, table in enumerate(doc.tables, start=1):
tblpr = table._tbl.tblPr
tblw = tblpr.find(qn("w:tblW"))
tblind = tblpr.find(qn("w:tblInd"))
grid_widths = [int(n.get(qn("w:w"))) for n in table._tbl.tblGrid.findall(qn("w:gridCol"))]
row_widths = []
for row in table.rows:
widths = []
for cell in row.cells:
tcw = cell._tc.get_or_add_tcPr().find(qn("w:tcW"))
widths.append(int(tcw.get(qn("w:w"))) if tcw is not None else None)
row_widths.append(widths)
expected = sum(grid_widths)
if tblw is None or int(tblw.get(qn("w:w"))) != expected:
issues.append(f"Table {index}: tblW/grid mismatch")
if expected != 9360:
issues.append(f"Table {index}: expected 9360 DXA, got {expected}")
if any(widths != grid_widths for widths in row_widths):
issues.append(f"Table {index}: tcW/grid mismatch")
table_geometry.append({
"table": index,
"rows": len(table.rows),
"cols": len(table.columns),
"width_dxa": expected,
"indent_dxa": int(tblind.get(qn("w:w"))) if tblind is not None else None,
})
result = {
"file": str(path),
"bytes": path.stat().st_size,
"paragraphs": len(doc.paragraphs),
"tables": len(doc.tables),
"scene_headings": len(scene_headings),
"prompt_sections": all_text.count("10秒图生视频提示词(可直接复制)"),
"fixed_10s_prompts": all_text.count("生成时长严格10秒"),
"table_geometry": table_geometry,
"issues": issues,
"status": "PASS" if not issues else "FAIL",
}
print(json.dumps(result, ensure_ascii=False, indent=2))
raise SystemExit(0 if not issues else 1)
+86
View File
@@ -0,0 +1,86 @@
from __future__ import annotations
import json
import zipfile
from pathlib import Path
from docx import Document
from docx.oxml.ns import qn
path = Path(r"D:\workspace\digital_me\artifacts\《我为你研墨》完整歌词脚本与20镜分镜提示词(每镜10秒).docx")
issues = []
with zipfile.ZipFile(path) as zf:
bad = zf.testzip()
media = [n for n in zf.namelist() if n.startswith("word/media/")]
if bad:
issues.append(f"Corrupt member: {bad}")
if media:
issues.append(f"Unexpected embedded images: {media}")
doc = Document(path)
text = "\n".join(p.text for p in doc.paragraphs)
for table in doc.tables:
for row in table.rows:
text += "\n" + "\t".join(cell.text for cell in row.cells)
scene_headings = [p.text for p in doc.paragraphs if p.style.name == "Heading 1" and p.text.startswith("分镜 ")]
if len(scene_headings) != 20:
issues.append(f"Expected 20 shot headings, found {len(scene_headings)}")
if text.count("10秒分镜提示词(可直接复制)") != 20:
issues.append("Expected 20 prompt sections")
if text.count("生成时长严格10秒") != 20:
issues.append("Every prompt is not explicitly fixed to 10 seconds")
lyrics = [
"你杀了我祖父的那一年", "我还不会写字", "如今我替你 把诏书写得漂亮",
"一笔一划 都是你要的锋芒", "掖庭里长大 罪臣的孙女", "我把恨 磨成了墨",
"一滴 不许它响", "该恨你 还是该谢你", "你毁了我的家 又给了我笔",
"我为你研墨 也为自己活", "仇人的江山 我替她说", "你看不透我 我也看不透我",
"这盘棋里 谁是谁的 因果", "掌上起风 婉儿落笔", "你不问我恨不恨 你只看我写得好不好",
"我也学会了 把心 藏进批注的小字号", "你老了 手也抖了 我扶你上朝",
"这一路 我们谁也没提过 那道诏", "祖父啊 我没替你报仇",
"我活成了 比报仇 更长的一句咒", "或者 只是一个 想活下去的人",
"墨干了 诏成了", "风 还在掌上",
]
missing = [line for line in lyrics if line not in text]
if missing:
issues.append(f"Missing lyric lines: {missing}")
for marker in ("TODO", "TBD", "【统一风格前缀】", "placeholder"):
if marker in text:
issues.append(f"Unexpected placeholder: {marker}")
tables = []
for index, table in enumerate(doc.tables, start=1):
tblpr = table._tbl.tblPr
tblw = tblpr.find(qn("w:tblW"))
grid = [int(x.get(qn("w:w"))) for x in table._tbl.tblGrid.findall(qn("w:gridCol"))]
expected = sum(grid)
if tblw is None or int(tblw.get(qn("w:w"))) != expected or expected != 9360:
issues.append(f"Table {index}: invalid total geometry")
for row in table.rows:
widths = []
for cell in row.cells:
tcw = cell._tc.get_or_add_tcPr().find(qn("w:tcW"))
widths.append(int(tcw.get(qn("w:w"))) if tcw is not None else None)
if widths != grid:
issues.append(f"Table {index}: cell/grid mismatch")
break
tables.append({"table": index, "rows": len(table.rows), "cols": len(table.columns), "width_dxa": expected})
result = {
"file": str(path),
"bytes": path.stat().st_size,
"paragraphs": len(doc.paragraphs),
"tables": tables,
"shot_headings": len(scene_headings),
"prompt_sections": text.count("10秒分镜提示词(可直接复制)"),
"fixed_10s_prompts": text.count("生成时长严格10秒"),
"embedded_images": 0,
"missing_lyrics": missing,
"issues": issues,
"status": "PASS" if not issues else "FAIL",
}
print(json.dumps(result, ensure_ascii=False, indent=2))
raise SystemExit(0 if not issues else 1)
+81
View File
@@ -0,0 +1,81 @@
import fs from 'node:fs'
import path from 'node:path'
import { execFileSync } from 'node:child_process'
const packageDirs = process.argv.slice(2).map((item) => path.resolve(item))
if (!packageDirs.length) {
console.error('Usage: node tools/validate_battle_asset_packs.mjs <package-dir> [...]')
process.exit(2)
}
const failures = []
const assert = (condition, message) => {
if (!condition) failures.push(message)
}
function dimensions(file) {
const output = execFileSync('sips', ['-g', 'pixelWidth', '-g', 'pixelHeight', file], { encoding: 'utf8' })
const width = Number(output.match(/pixelWidth: (\d+)/)?.[1])
const height = Number(output.match(/pixelHeight: (\d+)/)?.[1])
return { width, height }
}
for (const packageDir of packageDirs) {
const label = path.basename(packageDir)
const manifestPath = path.join(packageDir, 'manifest.json')
const readmePath = path.join(packageDir, '素材说明.md')
assert(fs.existsSync(manifestPath), `${label}: missing manifest.json`)
assert(fs.existsSync(readmePath), `${label}: missing 素材说明.md`)
if (!fs.existsSync(manifestPath)) continue
let manifest
try {
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
} catch (error) {
failures.push(`${label}: invalid JSON (${error.message})`)
continue
}
assert(manifest.event?.canonicalName === label, `${label}: canonicalName must match directory name`)
assert(manifest.narration?.language === 'zh-CN', `${label}: narration language must be zh-CN`)
assert(manifest.narration?.estimatedSeconds >= 60 && manifest.narration?.estimatedSeconds <= 90, `${label}: narration must be estimated at 60–90 seconds`)
assert(typeof manifest.narration?.fullText === 'string' && manifest.narration.fullText.length >= 180, `${label}: narration is unexpectedly short`)
assert(Array.isArray(manifest.scenes) && manifest.scenes.length >= 3 && manifest.scenes.length <= 5, `${label}: scene count must be 3–5`)
assert(Array.isArray(manifest.sources) && manifest.sources.length > 0, `${label}: package sources are required`)
assert(Array.isArray(manifest.blacklist) && manifest.blacklist.length > 0, `${label}: blacklist is required`)
assert(manifest.bgm?.catalogId?.length > 0, `${label}: BGM catalogId is required`)
const ids = new Set()
for (const scene of manifest.scenes || []) {
const prefix = `${label}/${scene.id || '?'}`
assert(!ids.has(scene.id), `${prefix}: duplicate scene id`)
ids.add(scene.id)
assert(scene.title && scene.timePlace && scene.purpose && scene.narration, `${prefix}: scene metadata incomplete`)
assert(scene.overlay?.heading && scene.overlay?.line, `${prefix}: overlay copy incomplete`)
assert(Array.isArray(scene.people) && scene.people.length > 0, `${prefix}: people tags required`)
assert(Array.isArray(scene.sources) && scene.sources.length > 0, `${prefix}: scene sources required`)
assert(scene.image?.alt && scene.image?.prompt && scene.image?.visualInference, `${prefix}: image metadata incomplete`)
for (const key of ['original', 'delivery']) {
const relative = scene.image?.[key]
assert(typeof relative === 'string' && relative.startsWith('images/'), `${prefix}: invalid image.${key}`)
if (!relative) continue
const file = path.join(packageDir, relative)
assert(fs.existsSync(file), `${prefix}: missing ${relative}`)
if (!fs.existsSync(file)) continue
const { width, height } = dimensions(file)
assert(width > 0 && height > 0, `${prefix}: could not read dimensions for ${relative}`)
assert(Math.abs(width / height - 16 / 9) < 0.002, `${prefix}: ${relative} is ${width}x${height}, not 16:9`)
}
}
console.log(`checked ${label}: ${manifest.scenes?.length || 0} scenes`)
}
if (failures.length) {
console.error(`\n${failures.length} validation failure(s):`)
for (const failure of failures) console.error(`- ${failure}`)
process.exit(1)
}
console.log(`all ${packageDirs.length} package(s) passed validation`)