anime: 补齐后续剧本与音色档案并整理仓库规范
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user