anime: 补齐后续剧本与音色档案并整理仓库规范

This commit is contained in:
2026-10-01 00:34:42 +08:00
parent 36c5febf0d
commit 0da2659df2
260 changed files with 38098 additions and 25 deletions
+26
View File
@@ -0,0 +1,26 @@
# 仓库工具
在仓库根目录使用PowerShell 7运行。工具不会自动提交或推送。
## 文档与设定校验
```powershell
./tools/validate-project.ps1
```
检查相对链接、年表、年龄、粮食算术、当前EP01 V05的25场/1290秒预算等。历史75秒技术试片单独校验,不混用当前第一集场号;通过不等于艺术质量、真实片长或配音验收通过。
## 图片素材站导出
```powershell
./tools/package-image-catalog.ps1 -PackageDate '2026-10-01'
```
- 不传日期时按中国标准时间生成当天目录;默认根目录取工具所在仓库,不依赖固定盘符。
- 输入为现存项目图片和本机已知桌面角色参考;外部参考分开,按内容去重但不删除原文件。
- 输出为 `06_AI动漫制作/07_交付与发行/素材网上传包_日期/`,含图片、同名TXT、CSV、JSON、映射及离线HTML目录;该复制包不入Git。
- [image_catalog_notes.json](image_catalog_notes.json)保存补充说明;[image_catalog_gallery.html](image_catalog_gallery.html)为离线目录模板。
- 同名目录已存在时默认停止;`-RefreshDescriptions`仅用于更新本工具生成包的说明,原图片校验不一致时拒绝覆盖。
- 公开发布前核对授权与PNG原生元数据;工具不调用网站接口,也不会把候选自动变成已批准素材。
2026-09-29的上传包已由作者使用后删除;本次只保留工具,不自动重新运行导出。
+33
View File
@@ -0,0 +1,33 @@
<!doctype html>
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>炉火|图片素材目录</title>
<style>
*{box-sizing:border-box}body{margin:0;background:#f5f2eb;color:#272c30;font:15px/1.7 system-ui,"Microsoft YaHei",sans-serif}header,main{max-width:1480px;margin:auto;padding:24px}header{padding-top:36px}h1{font-size:28px;margin:0}p{margin:8px 0;color:#61676a}.bar{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin:22px 0}input[type=search]{flex:1;min-width:250px}input,select,button{font:inherit;padding:8px 12px;border:1px solid #b8c0be;border-radius:8px;background:white}button{cursor:pointer}a{color:#286b67}#grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:18px}.card{background:white;border:1px solid #dedfd8;border-radius:12px;overflow:hidden}.card img{display:block;width:100%;height:270px;object-fit:contain;background:#e9eae4}.body{padding:15px}h2{font-size:16px;line-height:1.5;overflow-wrap:anywhere;margin:0 0 8px}.meta{font-size:12px;color:#586563}.tag{font-size:12px;border-radius:4px;background:#e5eeea;padding:3px 6px;display:inline-block;margin:2px}details{font-size:13px;margin-top:12px}summary{cursor:pointer;color:#286b67}.desc{white-space:pre-wrap;overflow-wrap:anywhere}.nav{display:flex;gap:16px;align-items:center;margin:20px 0}label{cursor:pointer}.small{font-size:13px}
</style></head><body><header><h1>炉火照彻诸神沉默的年代 · 图片素材</h1><p>原图保留 · 内容去重 · 每图说明 · 版本可追溯</p><p class="small">这是本地整理目录,不是定稿或授权证明。外部参考另存,不进入此图库。</p>
<div class="bar"><input type="search" id="query" placeholder="搜索人物、年龄、场景、镜号或说明…" aria-label="搜索素材"><select id="category" aria-label="分类"><option value="">全部分类</option></select><label><input id="all" type="checkbox"> 包含历史版、检查图和逐帧图</label></div>
<div class="small"><a href="素材清单.csv">CSV导入表</a> · <a href="素材清单.json">JSON完整数据</a> · <a href="README.md">整理说明</a></div><p id="count"></p></header>
<main><div class="nav"><button id="prev">上一页</button><span id="page"></span><button id="next">下一页</button></div><div id="grid"></div><p class="small">说明依据为已有项目记录与制作数据;展开可查看详细内容。图片未裁切、未修改。</p></main>
<script id="data" type="application/json">__CATALOG_DATA__</script>
<script>
const items=JSON.parse(document.getElementById('data').textContent),q=document.getElementById('query'),cat=document.getElementById('category'),all=document.getElementById('all'),grid=document.getElementById('grid');let page=0;const size=30;
for(const category of [...new Set(items.map(x=>x.category))]){const o=document.createElement('option');o.value=category;o.textContent=category;cat.append(o)}
function el(tag,text,cls){const n=document.createElement(tag);if(text)n.textContent=text;if(cls)n.className=cls;return n}
function render(){
const term=q.value.trim().toLowerCase();const selected=items.filter(x=>(all.checked||x.default_gallery_visible)&&(!cat.value||x.category===cat.value)&&(!term||[x.title,x.description,x.shot_id,x.filename].join(' ').toLowerCase().includes(term)));
const pages=Math.max(1,Math.ceil(selected.length/size));page=Math.max(0,Math.min(page,pages-1));
document.getElementById('count').textContent='当前显示 '+selected.length+' 张 / 全部 '+items.length+' 张(不含外部参考)。默认隐藏历史版、检查图、逐帧中间图。';
document.getElementById('page').textContent=(page+1)+' / '+pages;
document.getElementById('prev').disabled=page===0;document.getElementById('next').disabled=page>=pages-1;
grid.replaceChildren();
for(const x of selected.slice(page*size,(page+1)*size)){
const card=el('article',null,'card'),link=el('a');link.href=encodeURI(x.image_path);link.target='_blank';link.rel='noopener';
const img=el('img');img.src=encodeURI(x.image_path);img.alt=x.title;img.loading='lazy';link.append(img);card.append(link);
const body=el('div',null,'body');body.append(el('h2',x.title));body.append(el('span',x.category,'tag'),el('span',x.status,'tag'));
body.append(el('div',x.width+' × '+x.height+' · '+(x.bytes/1048576).toFixed(2)+' MB','meta'));
const details=el('details');details.append(el('summary','详细说明、用途与限制'),el('p',x.description,'desc'),el('p','原始来源:'+x.source_paths.join(';'),'desc'));
const txt=el('a','打开同名说明');txt.href=encodeURI(x.image_path.replace(/\.[^.]+$/,'.txt'));txt.target='_blank';details.append(txt);body.append(details);card.append(body);grid.append(card);
}
}
for(const input of [q,cat,all])input.addEventListener('input',()=>{page=0;render()});
document.getElementById('prev').onclick=()=>{page--;render()};document.getElementById('next').onclick=()=>{page++;render();window.scrollTo({top:0,behavior:'smooth'})};render();
</script></body></html>
+122
View File
@@ -0,0 +1,122 @@
{
"V06_尝试1": [
"维奥拉|V06比例修订尝试1",
"魔王紫发金眼、晶簇冠冕与宫廷礼服的比例修改中间候选;该文件标记体型仍偏长,用于与后续V06、V07对比。",
"历史尝试,不作为当前比例标准;原始图像未重新逐项测量。"
],
"V06_尝试2": [
"维奥拉|V06比例修订尝试2",
"魔王全身比例局部改善的第二次中间尝试,保留华丽衣装方向,属于头身比例探索过程。",
"历史中间稿;局部改善不代表定稿,当前外貌参考V07。"
],
"VIOLA_V04": [
"维奥拉|V04局部修订前",
"魔王V04阶段的旧版外形与服饰候选,保留当时的面部、发型和衣装探索,用于追溯后续服饰及比例修改。",
"归档旧图,不以本图衣装和比例覆盖V07。"
],
"四人合图": [
"核心四人|旧版画风合图",
"早期四名角色同框的风格探索合图,属于曾被作者否定画风方向的历史尝试。",
"文件已标明画风未通过,不推荐用于当前角色一致性输入。"
],
"first_frame_v02_style_review": [
"男主收手机|风格修订首帧V02",
"前世工厂更衣柜场景中的男主与手机首帧,作为收起手机动作的图生视频输入,V02用于对照画风修订。",
"手机界面应按母亲中秋消息的剧情处理;V02仍为测试候选,不因文件名而宣称通过。"
],
"first_frame_v01": [
"男主收手机|测试首帧V01",
"前世工厂更衣区的收手机动作首帧,服务于男主把私人通信收进储物柜的日常段落。",
"历史测试底图,后续有风格与动作版本;不以首帧代替已经完成的镜头动作。"
],
"CHR-POST-MC-A19": [
"埃利安·赫斯|19岁工匠",
"874年魔族边境落脚阶段的男主,灰黑发、深棕眼,系带内衣、长罩衣与短皮护裙。用于青年工匠身份、手工服装层次及跨镜形象对照。",
"不可替代870年15岁的少年版;没有魔法回路,不能因装饰或首帧效果新增施法能力。"
],
"CHR-POST-DK-A320": [
"维奥拉·阿斯特拉|320岁魔王",
"紫晶族魔王,紫发金眼、娇小成年体格,华丽紫黑金礼服、层叠长裙、披风与晶簇冠冕。礼仪服用于王庭正式接见,不直接用于下田和战斗。",
"冠冕是可摘饰物,不是天生角;种族无角、翼、尾。V07调整头部比例与面部柔和度,未最终定稿;颈部晶纹与冠形须补多角度核对。"
],
"CHR-LORD-TOK": [
"托克|铜炉伯",
"地精工匠领主,成年地精面部、后折耳与工匠工具;当前52岁服饰候选为铜褐织边罩袍、深绿内衣、可拆短皮护裙和手工护镜。",
"874年52岁、870年48岁;当前衣装不能套成现代维修马甲。护镜、卡尺与织边细节仍需动画化简化。"
],
"UNDEAD": [
"魔王直属骷髅军首领",
"不死族军团首领候选,裸骨头颅与骨手;当前服饰方向为黑铁胸甲、分节肩甲、灰白罩袍和梅紫披风,体现直属军团身份及后续建设功能。",
"暂名、纹章及饰物不等于正式设定;颞骨裂纹左右侧别尚未统一。不要与活体巨魔白骨侯乌尔加混淆。"
],
"CHR-EP01-MATTHIAS": [
"马提亚斯|45岁表情与全身参考",
"855年泉堂收留婴儿时的中年神父,朴素长袍披衣、克制表情,含站立全身与多个头部表情。",
"生成记录指出灰发偏多,与60岁版年龄差不足;正式生产前回调灰褐发色,不直接宣称年龄版已锁定。"
],
"CHR-EP01-MARA": [
"玛拉·苇田|58岁农妇",
"870年务实的佃农,灰褐发辫、晒斑、蓝色补丁披肩、旧鞋和湿土布包;全身与观察、质疑、微笑的表情参考。",
"是合作与实践角色,不是盲从男主的工具人;发丝与衣褶须跨角度统一,尚非完整三视图。"
],
"BG-EP01-CHAPEL": [
"圣伊诺泉堂|夜间无人空景",
"小型石木泉堂夜景,灰石水纹神像、木梁、长凳与黄烛;画左冷色窗光、画右照护侧室暖光,为婴儿祝福段落提供空间基准。",
"原记录尺寸1672×941,接近但非严格16:9;不得拉伸。窗尺度、旧裂、额外花与墙布待定,神像不说话、不活动。"
],
"MAP-WORLD": [
"已知世界总图|874年地理布局草案",
"原创奇幻世界地理图,人族大陆在西、魔族大陆在东,北部矮人山地、大精灵森林、中央狭海与商站;用于大陆关系、领地和旅行路线讨论。",
"V03为当前候选,具有人魔直接陆接带,区别于矮人山口绕行;V01/V02已过时。不按比例,小地名与路线细节待校排,不据此推算精确里程。"
],
"SC08_courtyard_departure": [
"大公府庭院|出发场景底图",
"第一集SC08大公府出发段落的构图与空间准备图,用于马车、庭院人物位置及父子告别镜头对照。",
"生产辅助图,不是独立角色定稿;出发装备与马车方向需与相邻镜头衔接。"
],
"CLERIC40_RAYMOND43": [
"寻伴神官与雷蒙德|庭院对照图",
"第一集庭院空间内的成年神官与43岁雷蒙德人物准备图,用于父亲与随行神官的比例、站位和衣装关联。",
"文件名中的CLERIC40是制作标签,不能据此直接把神官40岁写入小说正式设定。"
],
"ADA13_tutor_hall": [
"13岁艾达与带教|圣堂场景准备图",
"第一集圣堂会面段落中的少女治疗师与成人带教场景图,体现学习阶段与成人同行的关系。",
"这是后续生产辅助图,不等于此前被拦截的独立13岁角色设定图已完成或批准;不可用17岁版本混充本图年龄。"
],
"SC09_hall_master": [
"大圣堂|会面空间主参考",
"第一集SC09勇者与治疗师相识段落的圣堂空间准备图,用于人物站位、器物与光线连续性对照。",
"空间图不等于女神真实回应;神职人员的解释与女神意志须区分。"
],
"SC09_disk_activation": [
"寻伴仪|启动场景准备图",
"第一集SC09仪器与人物构图准备图,展示寻找队友相关器物的空间位置与使用场景。",
"器物并非现代GPS;图中自发文字、宗教标志及特效不能自动确认为设定。"
],
"SC08_Lucian_mounted": [
"少年卢西安|上马准备图",
"第一集SC08十四岁勇者出发时的骑乘场景图,用于少年体格、坐姿、马具和庭院构图衔接。",
"不要与874年十八岁青年角色图混用;手脚、缰绳及骑姿需逐镜检查。"
],
"SC10_Lucian_equipment": [
"少年卢西安|勇者装备准备图",
"第一集SC10交付勇者装备时的服装、装备与人物关系参考,用于镜头首帧和装备连续性。",
"装备纹章仍须与虚构教会标识统一,不将生成的符号当成正式纹章。"
],
"SC10_armor_fit": [
"勇者装备|试甲准备图",
"第一集SC10装备试穿与整理动作相关准备图,用于甲片层次、扣带、手部动作和人物站位。",
"甲具不能随镜头增减或穿模;图像不能代替完整动作验收。"
],
"SC10_Ada_cloth": [
"艾达|整理布料与药具准备图",
"第一集SC10治疗师衣物、布料及药具相关准备图,强调朴素医疗旅装与实际照料用途。",
"不以布料或器具的随机细节新增医疗技术;手部和器具结构需核对。"
],
"SC10_party_equipped": [
"勇者队伍|装备后同场准备图",
"第一集SC10队伍完成装备交接后的同场参考,用于勇者、治疗师与成人同行者的身高和衣装连续性。",
"同框不表示男主已经入队;具体人物关系以本场剧本为准。"
]
}
+288
View File
@@ -0,0 +1,288 @@
param(
[string]$ProjectRoot = (Split-Path -Parent $PSScriptRoot),
[string]$PackageDate = ([DateTimeOffset]::UtcNow.ToOffset([TimeSpan]::FromHours(8)).ToString('yyyy-MM-dd')),
[string]$PackageName = '',
[switch]$RefreshDescriptions
)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Drawing
$ProjectRoot = [IO.Path]::GetFullPath($ProjectRoot)
if (-not $PackageName) { $PackageName = '素材网上传包_' + $PackageDate }
if ($PackageName -notmatch '^素材网上传包_[^/\\:]+$') { throw 'PackageName必须是素材网上传包_开头的单个目录名' }
$dest = Join-Path $ProjectRoot "06_AI动漫制作/07_交付与发行/$PackageName"
$existingNames=@{}
if (Test-Path -LiteralPath $dest) {
if (-not $RefreshDescriptions) { throw "目标已存在,为保留已有交付请另用PackageName:$dest" }
$prior=Get-Content -LiteralPath "$dest/素材清单.json" -Raw | ConvertFrom-Json
if ($prior.schema_version -ne 1 -or $prior.project -ne '炉火照彻诸神沉默的年代') {throw '不是本工具的交付包'}
foreach($item in @($prior.items)+@(Get-Content -LiteralPath "$dest/外部参考清单.json" -Raw | ConvertFrom-Json)) {$existingNames[$item.sha256]=$item.filename}
}
$notes = Get-Content -LiteralPath (Join-Path $PSScriptRoot 'image_catalog_notes.json') -Raw | ConvertFrom-Json -AsHashtable
$episodeRoot = Join-Path $ProjectRoot '06_AI动漫制作/05_生产素材/正片/S01E01_H3_V01'
$manifest = Get-Content -LiteralPath "$episodeRoot/source_manifest.json" -Raw | ConvertFrom-Json
$shotMap = @{}; foreach ($shot in $manifest.shots) { $shotMap[$shot.id] = $shot }
$sceneNames = @{1='前世租屋';2='化工厂交班与更衣';3='事故与救人';4='婴儿与泉堂外景';5='孤儿院照料婴儿';6='泉堂祝福与沉默';7='孤儿院日常';8='大公府出发';9='大圣堂会面';10='勇者装备交付';12='少年与送货'}
$linked = @{}
$indexPath = Join-Path $ProjectRoot '06_AI动漫制作/02_视觉圣经/角色设定/角色参考图索引.md'
foreach ($m in [regex]::Matches([IO.File]::ReadAllText($indexPath),'\[([^\]]+)\]\(([^)]+\.png)\)')) {
$target = [IO.Path]::GetFullPath((Join-Path (Split-Path $indexPath) $m.Groups[2].Value))
$linked[$target] = $m.Groups[1].Value
}
$reviewNotes = @{}
$reviewDoc = Join-Path $ProjectRoot '06_AI动漫制作/02_视觉圣经/角色设定/2026-09-29_其余角色逐张生成_V08.md'
foreach ($line in Get-Content -LiteralPath $reviewDoc) {
if ($line -match '^\| \d+ \| ([^|]+) \| ([^|]+) \|') { $reviewNotes[$Matches[1].Trim()] = $Matches[2].Trim() }
}
Push-Location $ProjectRoot
try { $paths = @(rg --files --hidden --no-ignore -g '*.png' -g '*.jpg' -g '*.jpeg' -g '*.webp' -g '*.gif' -g '*.bmp' -g '!.git/**' -g '!06_AI动漫制作/07_交付与发行/**') } finally { Pop-Location }
$sources = [Collections.Generic.List[object]]::new()
foreach ($rel in $paths) {
$sources.Add([pscustomobject]@{Full=(Join-Path $ProjectRoot $rel);Rel=$rel.Replace('\','/');External=($rel -match '画风参考/|画风参考\\|用户提供');Origin='项目'})
}
$desktop = 'C:\Users\zhang\Desktop\AI短剧\角色管理'
if (Test-Path -LiteralPath $desktop) {
foreach ($f in Get-ChildItem -LiteralPath $desktop -File -Recurse | Where-Object Extension -in @('.png','.jpg','.jpeg','.webp')) {
$sources.Add([pscustomobject]@{Full=$f.FullName;Rel=('桌面角色管理/'+[IO.Path]::GetRelativePath($desktop,$f.FullName).Replace('\','/'));External=$true;Origin='桌面来源待核参考'})
}
}
foreach ($s in $sources) { $s | Add-Member Hash (Get-FileHash -LiteralPath $s.Full -Algorithm SHA256).Hash.ToLowerInvariant() }
$groups = @($sources | Group-Object Hash)
$generated = [Collections.Generic.List[object]]::new()
$external = [Collections.Generic.List[object]]::new()
$copyJobs = [Collections.Generic.List[object]]::new()
function CleanName([string]$value) {
$clean = [regex]::Replace($value,'[<>:"/\\|?*\x00-\x1f]','_')
$clean = [regex]::Replace($clean,'\s+','_').Trim('.',' ')
if ($clean.Length -gt 88) { $clean = $clean.Substring(0,88) }
return $clean
}
function ShortText([string]$value,[int]$max=1200) {
$value = ($value -replace '\r?\n',' ' -replace '\s+',' ').Trim()
if ($value.Length -gt $max) { return $value.Substring(0,$max)+'…' }
return $value
}
foreach ($group in $groups) {
$ordered = @($group.Group | Sort-Object @{Expression={$_.External}},@{Expression={$_.Rel -match '^99_|post_frames|review|rejected'}},Rel)
$s = $ordered[0]; $rel=$s.Rel; $f=Get-Item -LiteralPath $s.Full; $stem=$f.BaseName
$isExternal = @($ordered | Where-Object {-not $_.External}).Count -eq 0
$category='生成辅助图'; $status='生产素材待审'; $use='构图、画面连续性与图生视频输入参考;入正式资产库前审看。'
$prefix='02'; $title=$stem; $detail=''; $warn='图片存在不等于已通过艺术或连续性验收。'; $context=''; $shotId=''; $age='未在本图单独核定'
$evidence=[Collections.Generic.List[string]]::new(); $evidence.Add($rel)
$assetId=if ($stem -match '^(CHR|REF|BG|MAP)-') { $stem } else { '' }
$isArchived=$rel.StartsWith('99_')
if ($isExternal) {
$category='外部参考';$status='非本项目生成/来源授权待核';$prefix='90'
$title='外部参考|'+$stem
$detail=if ($rel -match '画风参考|用户提供') {'用户提供的既有动画画风截图,仅作线条、色彩和光影讨论。'} else {'桌面角色管理目录中的原有参考图;未找到足够证据将其认定为本项目新生成的原创素材。'}
$warn='不要按本项目原创生成图公开发布或出售;来源及授权待核实。';$use='私人参考索引,不默认上传。'
} elseif ($rel -match 'post_frames/') {
$category='视频逐帧中间图';$prefix='06';$status='后期中间图/非独立原画'
$title=([regex]::Match($rel,'E01-S\d+-C\d+').Value)+'|'+$stem
$detail='视频后期逐帧序列中的单帧,供屏幕内容合成、画面检查或后期追溯;相邻文件通常属于同一镜头,不应全部作为不同原创设定图。'
$warn='帧序号是文件编号,不直接推算时间码;原视频帧率与提取起点须另查。';$use='技术归档;默认预览隐藏,不建议逐张公开上架。'
} elseif ($stem -match '^(review|compare|waveform)|_sheet$|检查|抽帧|light_controlled_review' -or $rel -match '/scenes/') {
$category=if ($stem -eq 'waveform') {'音频波形图'} else {'检查拼图或抽帧'}
$prefix='05';$status='质检辅助/非独立原画'
$title=if ($rel -match 'E01-S\d+-C\d+') { $Matches[0]+'|'+$stem } elseif($stem -eq 'waveform') { (Split-Path (Split-Path $s.Full) -Leaf)+'|波形图' } else {$stem}
$detail='制作过程中生成的检查、比较或预览图片,用于画面节奏、构图或技术效果对照。'
$warn='可能含多帧、标注或技术图表;不能当作单帧设定图或完整成片。';$use='内部审看与制作过程记录。'
}
if (-not $isExternal -and ($rel -match '/角色设定/' -or $stem -match '^CHR-|^REF-' -or $isArchived)) {
$category='角色参考图';$prefix='01';$status='REVIEW/候选未定稿'
if ($linked.ContainsKey($s.Full)) { $title=$linked[$s.Full] }
elseif ($rel -match '/([^/]+)/(?:01_参考图|02_生成候选)/') { $title=$Matches[1]+'|'+$stem }
$detail='用于角色身份、服装、种族轮廓和年龄版本对照的生成候选。'
$warn='图中自发纹章、文字与装饰不自动成为小说设定;没有完整侧背、表情和跨镜测试。'
$parent=Split-Path (Split-Path $s.Full)
$design=Join-Path $parent '视觉设计方案.md'
if (-not (Test-Path -LiteralPath $design) -and $rel -match '(0[12]_转生[前后]角色/.*)/(01_参考图|02_生成候选)/') {
$design=Join-Path $ProjectRoot ('06_AI动漫制作/02_视觉圣经/角色设定/'+$Matches[1]+'/视觉设计方案.md')
}
if (Test-Path -LiteralPath $design) {
$bullets=@(Get-Content -LiteralPath $design | Where-Object {$_ -match '^- (性别|视觉年龄|骨相|体型|服装|道具|动作|不可漂移|种族|发色|瞳色)'})
$bullets=@($bullets | Where-Object {$_ -notmatch '\d+岁|真实年龄'})
$context='通用角色卡供对照,旧版服装条目以本图生成记录覆盖:'+(ShortText ($bullets -join ' ') 1700)
$evidence.Add([IO.Path]::GetRelativePath($ProjectRoot,$design).Replace('\','/'))
}
foreach ($k in @($reviewNotes.Keys | Sort-Object Length -Descending)) { if ($title -like "*$k*" -and $stem -match 'V08') { $detail += ' 生成记录的画面描述与初检:'+$reviewNotes[$k];$evidence.Add([IO.Path]::GetRelativePath($ProjectRoot,$reviewDoc).Replace('\','/'));break } }
}
foreach ($k in @($notes.Keys | Sort-Object Length -Descending)) {
if (-not $isExternal -and $stem.Contains($k)) { $title=$notes[$k][0]+'|'+$stem;$detail=$notes[$k][1];$warn=$notes[$k][2];break }
}
if (-not $isExternal -and $stem -match '^BG-') { $category='场景设定图';$prefix='02';$status='REVIEW/候选未定稿' }
if (-not $isExternal -and $stem -match '^MAP-') { $category='世界地图';$prefix='03';$status='REVIEW/布局草案';$use='地理布局、商路和行军关系讨论,不用于精确测距。' }
if ($stem -match '(?:-|_)A(\d+)(?:-|_)') { $age=$Matches[1]+'岁(文件所标版本;归档图不保证符合现行时间线)' }
if ($isArchived) {
$prefix='04';$status='ARCHIVED/历史版本'
$warn='已被后续方案替代或仍有问题,不作为当前生产底图。 '+$warn
$use='设计迭代追溯、旧版对比;上传时必须保留“历史版本”标签。'
if ($rel -match '服饰修订前') { $warn+=' 此批衣装与世界观时代、阶层要求不符,后续已经修订。' }
if ($rel -match '魔王比例修订前') { $warn+=' 魔王头身比例或双视图比例有问题;当前参照V07而不是此图。' }
if ($rel -match '魔王外貌微调前') { $warn+=' 此图早于V07头部缩小及衣装前片修订。' }
if ($rel -match '地图候选') { $warn+=' 北部直连关系不足,当前已改为V03。' }
if ($rel -match '角色细节修订前') { $warn+=' 这是V08B之前的局部问题版本;萨尔克靴/手、神父年龄或奈芙拉肢体读图需参照修订。' }
}
if ($rel -match '(E01-S\d+-C\d+)') {
$shotId=$Matches[1]
if ($shotMap.ContainsKey($shotId)) {
$sh=$shotMap[$shotId];$sceneName=$sceneNames[[int]$sh.scene]
$context="原镜头:$shotId;场景:$sceneName;景别机位:$($sh.framing);计划画面与表演:$($sh.action_performance);空间光线:$($sh.space_light);声音关系:$($sh.sound)。这是原制作说明,不保证每项均已在图片中正确实现。"
$evidence.Add('06_AI动漫制作/05_生产素材/正片/S01E01_H3_V01/source_manifest.json')
if ($category -eq '生成辅助图') { $title="$shotId|$sceneName|$stem";$detail="第一集对应镜头的生成首帧或返修底图。"+$sh.action_performance }
}
$readyPath=Join-Path (Split-Path $s.Full) 'ready.json'
if (Test-Path -LiteralPath $readyPath) {
$ready=Get-Content -LiteralPath $readyPath -Raw | ConvertFrom-Json
if ($ready.frame_qc.notes) { $warn+=' 原首帧审看记录:'+$ready.frame_qc.notes }
}
}
if ($rel -match 'rejected|first_v1') { $status='历史或返修前版本/复核';$warn+=' 文件位于返修或旧版路径,不按最新版自动使用。' }
if (-not $detail) { $detail='本项目制作图片。独立画面内容尚无充分文字记录,请结合原图核对后补充;不从文件名编造人物身份或事件。' }
if ($rel -match '视频测试' -and -not $shotId) {
if ($rel -match '男主收手机') { $context='前世男主在化工厂更衣柜前收手机的测试段;母亲中秋消息通过屏幕文字表现,不另配母亲旁白。' }
elseif ($rel -match '泉堂夜景|SC06_40秒') { $context='泉堂祝福测试段,855年的45岁马提亚斯、13岁阿黛尔与婴儿;冷窗光与暖烛光,神像保持静止与沉默。' }
elseif ($rel -match '开篇40秒') { $context='前世租屋开篇40秒柔光测试,风扇、水杯、电脑和29岁男主的日常。测试版不自动替代正片同名镜头。' }
$use+=' 测试素材与正式镜头身份分开登记。'
}
if ($rel -match '视频测试/.+/(?:frames/)?(C0[1-8])(?:R2)?(?:_|/|\.)' -and $rel -match 'SC06_40秒|开篇40秒') {
$testId=$Matches[1]
$testMatch=[regex]::Match($rel,'/(C0[1-8])(?:R2)?(?:_|/|\.)')
$testId=$testMatch.Groups[1].Value
if($rel -match 'SC06_40秒') {
$testText=@{C01='马提亚斯怀抱婴儿来到泉堂神像前,阿黛尔持烛在旁,左窗冷光与右侧暖室构成静默空间。';C02='45岁神父怀抱婴儿祷告的近景,银白反光打断话语,眼神抬向神像方向。';C03='灰石神像水纹与婴儿祝福印记的近景,光是细微回应,不是神像活化或女神开口。';C04='13岁阿黛尔双手持烛的近景,视线由烛火转向画外神父与神像,表现好奇和探寻。';C05='神父等待回应的表情近景,抱婴姿势稳定,期待逐渐转为接受沉默。';C06='神父将目光重新落回婴儿、少女跟随注视的双人场面,祝福余光退去,回到实际照料。';C07='泉堂仪式之后的延续场面,以仍在燃烧的烛火和人物实际照料维持日常,没有女神额外发言。';C08='祝福事件后的记录与报告场面,供边境发现向大圣堂上报的段落衔接。'}
} else {
$testText=@{C01='租屋窗边风扇、半杯水与薄荷盆栽近景,冷暖柔光表现普通生活。';C02='前世男主从左肩后看电脑分类结果,屏幕中的猫是静态图片,鼠标与错误识别服务轻微幽默。';C03='29岁男主看向右侧电脑的侧面近景,克制笑意与自然疲倦构成普通青年形象。';C04='双手与笔记本的俯拍近景,未完成计算、铅笔和纸页用于呈现广泛但仍在试错的爱好。';C05='男主听见闹钟后停下电脑操作的侧面中景,收手和肩部放松表示结束折腾、准备休息。';C06='男主在窗边查看稀疏的第三盆薄荷,杯与土壤形成浇水动作起点,植物不神奇生长。';C07='浇水后拉窗帘、准备走向床铺的租屋全景,窗、家具和人物动线保持连续。'}
}
if($testText.ContainsKey($testId)) {
if($category -eq '生成辅助图') {$detail=$testText[$testId]+' 这是该测试镜头的输入或辅助静图,动态完成度另查视频。';$title=$testId+'|'+$(if($rel -match 'SC06_40秒'){'泉堂祝福测试'}else{'前世租屋测试'})+'|'+$stem}
else {$detail+=' 对应镜头内容:'+$testText[$testId]}
}
}
$img=[Drawing.Image]::FromFile($s.Full)
try { $width=$img.Width;$height=$img.Height;$format=$f.Extension.ToLowerInvariant() } finally { $img.Dispose() }
$hash=$group.Name
$imageName=if($existingNames.ContainsKey($hash)) {$existingNames[$hash]} else {(CleanName ("$prefix"+'__'+$title))+'__'+$hash.Substring(0,10)+$format}
$folder=if ($isExternal) {'外部参考_不默认上传'} else {'图片'}
$outRel="$folder/$imageName"
$srcRels=@($ordered.Rel)
$description="《炉火照彻诸神沉默的年代》素材。$detail "+$(if($context){"设定/制作背景(非逐项视觉验收):$context "})+"状态:$status。用途:$use 注意:$warn"
$record=[pscustomobject][ordered]@{
asset_id=('IMG-'+$hash.Substring(0,16));original_asset_id=$assetId;filename=$imageName;image_path=$outRel;
title=$title;category=$category;status=$status;description=$description;content_description=$detail;
context_notes=$context;usage=$use;limitations=$warn;age_version=$age;shot_id=$shotId;
tags=@('炉火照彻诸神沉默的年代','AI动漫',$category,$status);width=$width;height=$height;bytes=$f.Length;
sha256=$hash;original_filename=$f.Name;source_paths=$srcRels;evidence_paths=@($evidence);
description_basis='依据项目索引、已有生成记录及制作数据整理;不是全图重新逐张验收';
public_release_approved=$false;rights_note='本包不作版权或商业授权保证;外部参考与生成素材分别标记';
default_gallery_visible=($prefix -in @('01','02','03'));duplicate_source_count=$srcRels.Count
}
$copyJobs.Add([pscustomobject]@{Source=$s.Full;Relative=$outRel;Record=$record})
if ($isExternal) { $external.Add($record) } else { $generated.Add($record) }
}
$generated=@($generated | Sort-Object category,title,asset_id)
$external=@($external | Sort-Object title)
foreach($folder in @("$dest/图片","$dest/外部参考_不默认上传")) {if(-not(Test-Path -LiteralPath $folder)){New-Item -ItemType Directory -Path $folder | Out-Null}}
$utf8=[Text.UTF8Encoding]::new($false)
foreach ($job in $copyJobs) {
$target=Join-Path $dest $job.Relative
if(Test-Path -LiteralPath $target){
if((Get-FileHash -LiteralPath $target -Algorithm SHA256).Hash.ToLowerInvariant() -ne $job.Record.sha256){throw "图片内容已改变,不覆盖:$target"}
}else{Copy-Item -LiteralPath $job.Source -Destination $target}
$r=$job.Record
$text=@"
标题:$($r.title)
素材编号:$($r.asset_id)
原资产编号:$($r.original_asset_id)
图片文件:$($r.filename)
分类:$($r.category)
状态:$($r.status)
年龄版本:$($r.age_version)
镜头编号:$($r.shot_id)
【内容说明】
$($r.content_description)
【设定与制作背景】
$($r.context_notes)
【建议用途】
$($r.usage)
【版本问题与使用限制】
$($r.limitations)
【尺寸与来源】
尺寸:$($r.width) × $($r.height) 像素
文件字节数:$($r.bytes)
SHA-256:$($r.sha256)
原文件名:$($r.original_filename)
全部来源:
$($r.source_paths -join [Environment]::NewLine)
说明依据:$($r.description_basis)
公开发布批准:尚未确认;本包仅供整理和选择上传
来源授权说明:$($r.rights_note)
【素材网站可用简介】
$($r.description)
标签:$($r.tags -join ',')
"@
[IO.File]::WriteAllText([IO.Path]::ChangeExtension($target,'.txt'),$text,$utf8)
}
$catalog=[pscustomobject]@{schema_version=1;project='炉火照彻诸神沉默的年代';packaged_date=$PackageDate;source_image_files=$sources.Count;project_source_files=$paths.Count;unique_project_images=$generated.Count;unique_external_references=$external.Count;duplicate_copies_removed=$sources.Count-$groups.Count;items=$generated}
[IO.File]::WriteAllText("$dest/素材清单.json",($catalog | ConvertTo-Json -Depth 12),$utf8)
[IO.File]::WriteAllText("$dest/外部参考清单.json",(@($external) | ConvertTo-Json -Depth 12),$utf8)
$csv=@($generated | ForEach-Object { [pscustomobject][ordered]@{素材编号=$_.asset_id;文件名=$_.filename;图片路径=$_.image_path;标题=$_.title;分类=$_.category;版本状态=$_.status;详细说明=$_.description;标签=($_.tags -join ';');宽度=$_.width;高度=$_.height;字节数=$_.bytes;原资产编号=$_.original_asset_id;年龄版本=$_.age_version;镜头编号=$_.shot_id;原路径=($_.source_paths -join ';');SHA256=$_.sha256;公开发布已批准='否';说明依据=$_.description_basis} })
$csv | Export-Csv -LiteralPath "$dest/素材清单.csv" -NoTypeInformation -Encoding utf8BOM
$generated | Where-Object default_gallery_visible | Select-Object asset_id,filename,title,category,status | Export-Csv -LiteralPath "$dest/优先审看清单.csv" -NoTypeInformation -Encoding utf8BOM
$sourceMap=foreach($r in @($generated)+@($external)) {foreach($p in $r.source_paths){[pscustomobject]@{原路径=$p;目标路径=$r.image_path;SHA256=$r.sha256;处理='原文件保留;内容相同仅输出一份'}}}
$sourceMap | Export-Csv -LiteralPath "$dest/原图到上传文件映射.csv" -NoTypeInformation -Encoding utf8BOM
$escapedJson=(@($generated) | ConvertTo-Json -Depth 10 -Compress).Replace('<','\u003c')
$template=[IO.File]::ReadAllText((Join-Path $PSScriptRoot 'image_catalog_gallery.html'))
[IO.File]::WriteAllText("$dest/图片预览目录.html",$template.Replace('__CATALOG_DATA__',$escapedJson),$utf8)
$counts=($generated | Group-Object category | ForEach-Object {"- $($_.Name):$($_.Count)张"}) -join [Environment]::NewLine
$readme=@"
# 素材网上传包
项目:《炉火照彻诸神沉默的年代》;整理日期:$PackageDate。
## 直接使用
- **图片/**:$($generated.Count)张内容去重后的本项目图片全部集中在这一个文件夹;每张旁边有同名TXT详细说明。
- **图片预览目录.html**:双击可离线打开,按标题/说明/分类搜索;默认只显示当前人物、地图与生产底图,勾选可显示历史和中间图。
- **素材清单.csv**:UTF-8 BOM,通用网站批量导入字段;用表格软件打开时选UTF-8。
- **素材清单.json**:包含图片、标题、完整说明、标签、尺寸、来源、版本、SHA-256。
- **优先审看清单.csv**:便于先审核候选原画,不代表已批准公开发布。
- **原图到上传文件映射.csv**:原路径到唯一图片的映射,不遗漏重复副本来源。
- **外部参考_不默认上传/**:$($external.Count)张外部截图或来源未核的桌面参考,另附说明,不混入生成图库。
## 盘点与范围
扫描仓库已有图片$($paths.Count)个,加已知桌面角色参考$($sources.Count-$paths.Count)个。以文件内容SHA-256去重;相同画面但字节不同的图片仍分别保留,不做相似图删除。
合计保留$($groups.Count)份唯一图片,其中本项目$($generated.Count)张、外部参考$($external.Count)张;去掉$($sources.Count-$groups.Count)个重复副本的额外拷贝,原文件全部不动。仅整理本地落盘项目文件,未声称扫描整台电脑、所有临时生成目录或服务器全部图片。
没有重画、裁切、压缩或放大图片,目标图片与来源字节一致;没有上传网站、提交或推送。
## 本项目图片分类
$counts
## 上传前注意
1. 当前人设仍为REVIEW,不因整理自动成为定稿。旧版图保留ARCHIVED;同名人物的年龄版不能混用。
2. 说明来自原生成记录、项目设定与镜头资料;没有声称重新逐张通过视觉质检。设定背景是目标,不保证画面正确画出所有细节。
3. 逐帧图、波形、拼图和检查图只宜技术归档,不能包装成独立原创设定图。网站可用分类过滤。
4. 外部动画截图和桌面来源待核参考不进入主导入表。生成素材的具体授权仍需按你的实际服务与用途确认,本包不宣称授权已经审核。
5. 不上传工作流、服务地址、凭据或服务器配置;图片保留原字节,PNG可能仍有原生元数据,公开发布前按素材站隐私要求检查。
6. CSV为通用字段,并非已适配某个站点接口;导入时映射“图片路径、标题、详细说明、标签”。TXT可直接复制文案。
"@
[IO.File]::WriteAllText("$dest/README.md",$readme,$utf8)
$failures=[Collections.Generic.List[string]]::new()
foreach($job in $copyJobs){
$target=Join-Path $dest $job.Relative
if ((Get-FileHash -LiteralPath $target -Algorithm SHA256).Hash.ToLowerInvariant() -ne $job.Record.sha256) {$failures.Add($job.Relative)}
if (-not (Test-Path -LiteralPath ([IO.Path]::ChangeExtension($target,'.txt')))) {$failures.Add('missing description '+$job.Relative)}
}
$validation=[pscustomobject]@{image_files=$groups.Count;main_images=$generated.Count;external_images=$external.Count;descriptions=$groups.Count;csv_rows=@(Import-Csv -LiteralPath "$dest/素材清单.csv").Count;source_mapping_rows=@($sourceMap).Count;hash_mismatches=@($failures);source_files_untouched=$true}
[IO.File]::WriteAllText("$dest/校验报告.json",($validation | ConvertTo-Json -Depth 5),$utf8)
[pscustomobject]@{destination=$dest;validation=$validation;categories=@($generated | Group-Object category | Select-Object Count,Name)} | ConvertTo-Json -Depth 6
+4 -4
View File
@@ -74,7 +74,7 @@ Write-Output "V03 chronology: $($v3Checks.Count) anchors; early arc and blessing
$continuityChecks = @(
@{ Path = '03_故事大纲/魔族落脚与魔王结缘_V03补充.md'; Terms = @('托克', '约六周', '874年晚秋', '875年秋', '唯一的救赎之光') },
@{ Path = '02_世界设定/女神祝福与寻伴仪.md'; Terms = @('魔族高阶种族', '魔王维奥拉', '不必托克替她鉴定') },
@{ Path = '02_世界设定/魔族食物与粮食来源.md'; Terms = @('购买的粮食', '魔兽肉', '雄厚矿脉', '人类大陆稀有', '仍要购买粮食') },
@{ Path = '02_世界设定/魔族食物与粮食来源.md'; Terms = @('经中转购入的粮食', '魔兽肉', '矿脉雄厚', '教会知道', '未使大陆自给') },
@{ Path = '02_世界设定/幕纹石与隔魔农业.md'; Terms = @('正式设定 V1.0', '868年', '适魔,不是彻底无魔', '本地专家', '不是吸能电池', '不能抵挡集中的战斗魔流', '黛拉', '瑟恩', '诺尔') },
@{ Path = '01_角色卡/男主.md'; Terms = @('社畜', '不愿往管理岗升', '训练人工智能', '不愿靠压迫别人', '广而不精', '无回路') }
)
@@ -106,10 +106,10 @@ if (-not $fieldArea.Success -or 4 * [int]$fieldArea.Groups[1].Value -ne [int]$fi
}
Write-Output 'First-harvest and four-field arithmetic checked (fictional setting, not real yield validation)'
$scriptText = Read-ProjectText '06_AI动漫制作/01_改编开发/03_动画剧本/S01E01_留给你的那一碗_V03.fountain'
$scriptText = Read-ProjectText '06_AI动漫制作/01_改编开发/03_动画剧本/S01E01_留给你的那一碗_V05.fountain'
$sceneIds = @([regex]::Matches($scriptText, '(?m)^(?:INT\.|EXT\.|INT\./EXT\.)[^\r\n]*#(01-\d{2})#\s*$') | ForEach-Object { $_.Groups[1].Value })
$expectedSceneIds = @(1..16 | ForEach-Object { '01-{0:D2}' -f $_ })
if (($sceneIds -join ',') -ne ($expectedSceneIds -join ',')) { $failures.Add('Script scenes must be ordered 01-01 through 01-16') }
$expectedSceneIds = @(1..25 | ForEach-Object { '01-{0:D2}' -f $_ })
if (($sceneIds -join ',') -ne ($expectedSceneIds -join ',')) { $failures.Add('V05 script scenes must be ordered 01-01 through 01-25') }
$outline = Read-ProjectText '06_AI动漫制作/01_改编开发/02_分集大纲/S01E01_分场大纲.md'
$sceneRows = @([regex]::Matches($outline, '(?m)^\| (01-\d{2}) \|[^|]+\| (\d+) \|'))