43 lines
5.3 KiB
Python
43 lines
5.3 KiB
Python
"""生成本地双版本导航与真人素材审看页,不依赖网络。"""
|
||
import csv
|
||
import html
|
||
import io
|
||
import json
|
||
from pathlib import Path
|
||
|
||
ROOT=Path(__file__).resolve().parent.parent
|
||
LIVE=ROOT/'08_真人AI短剧'
|
||
assets=json.loads((LIVE/'02_资产/全部资产任务.json').read_text(encoding='utf8'))
|
||
receipts={}
|
||
notes_path=LIVE/'05_检查与交付/图像审看提示.json'
|
||
notes=json.loads(notes_path.read_text(encoding='utf8')) if notes_path.exists() else {}
|
||
for p in (LIVE/'02_资产/生成回执').glob('*.json'):
|
||
r=json.loads(p.read_text(encoding='utf8'));receipts[r['id']]=r
|
||
cards=[];rows=[]
|
||
for a in assets:
|
||
r=receipts.get(a['id']);exists=bool(r and (LIVE/r['path']).is_file())
|
||
status='REVIEW' if exists else '待生成'
|
||
kind='角色' if a['kind']=='character' else '场景'
|
||
picpath=r['path'] if exists else ''
|
||
note=notes.get(a['id'],'')
|
||
row={'资产ID':a['id'],'名称':a['name'],'类别':kind,'状态':status,'图片':picpath, '提示词':'04_生成与后期/逐项出图提示词/'+a['id']+'.txt','原生尺寸':'×'.join(map(str,r['size'])) if exists and r.get('size') else '', '身份':a.get('identity',''),'审看提示':note,'角色批准':'未批准','动作验证':'未测试'}
|
||
rows.append(row)
|
||
title=html.escape(a['name']);aid=html.escape(a['id'])
|
||
picture=f'<a href="{html.escape(picpath)}"><img loading="lazy" src="{html.escape(picpath)}" alt="{title}"></a>' if exists else '<div class="pending">待生成</div>'
|
||
note_html=f'<p class="note">{html.escape(note)}</p>' if note else ''
|
||
identity_html=f'<p>{html.escape(a.get("identity",""))}</p>' if a.get('identity') else ''
|
||
cards.append(f'<article data-kind="{kind}" data-status="{status}" data-search="{html.escape(a["name"]+" "+a["id"]+" "+a.get("identity",""))}">{picture}<h2>{title}</h2><p>{aid}</p>{identity_html}<p>{kind} · {status}</p>{note_html}<a href="{html.escape(row["提示词"])}">查看完整提示词</a></article>')
|
||
done=sum(r['状态']=='REVIEW' for r in rows)
|
||
page='''<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>真人AI短剧素材审看</title><style>
|
||
body{margin:0;background:#11151a;color:#f0ede5;font:16px/1.6 system-ui,"Microsoft YaHei",sans-serif}header{padding:38px max(5vw,22px);background:linear-gradient(110deg,#233d3e,#161c29)}h1{font-size:30px;margin:0 0 8px}a{color:#bde7d7}nav{display:flex;flex-wrap:wrap;gap:18px}section{padding:24px max(5vw,22px)}.controls{display:flex;gap:12px;flex-wrap:wrap}input,select{font:inherit;padding:9px 12px;background:#242b33;border:1px solid #50636b;color:#fff;border-radius:6px}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:22px}article{background:#20272e;border:1px solid #354148;padding:12px;border-radius:10px}img,.pending{width:100%;height:300px;object-fit:contain;background:#13181d}.pending{display:grid;place-items:center;color:#9ba6ac}h2{font-size:18px;margin:12px 0 5px}article p{font-size:12px;color:#c6ced1;word-break:break-all;margin:5px 0}[hidden]{display:none!important}</style>
|
||
<header><h1>真人AI奇幻短剧素材审看</h1><p>COUNT · 每项可查看原图与实际出图提示词。REVIEW表示已出候选,尚未作者批准或动作验证。</p><nav><a href="README.md">真人制作入口</a><a href="01_剧本/第一季12集_真人阅读合订本.md">12集完整剧本</a><a href="../影像制作总览.md">双版本总览</a><a href="../06_AI动漫制作/README.md">动漫制作入口</a></nav></header>
|
||
<section class="controls"><input id="q" placeholder="搜索角色 场景 名称或编号"><select id="kind"><option value="">全部类别</option><option>角色</option><option>场景</option></select><select id="status"><option value="">全部状态</option><option>REVIEW</option><option>待生成</option></select><span id="visible"></span></section><section class="grid">CARDS</section><script>
|
||
const q=document.querySelector('#q'),kind=document.querySelector('#kind'),status=document.querySelector('#status'),items=[...document.querySelectorAll('article')];function filter(){let n=0;for(const x of items){const ok=x.dataset.search.toLowerCase().includes(q.value.toLowerCase())&&(!kind.value||x.dataset.kind===kind.value)&&(!status.value||x.dataset.status===status.value);x.hidden=!ok;if(ok)n++}document.querySelector('#visible').textContent='显示 '+n+' 项'}for(const x of [q,kind,status])x.addEventListener('input',filter);filter();</script></html>'''
|
||
page=page.replace('COUNT',f'已生成 {done} / {len(assets)} 项').replace('CARDS','\n'.join(cards))
|
||
(LIVE/'素材审看.html').write_text(page,encoding='utf8')
|
||
out=io.StringIO(newline='');w=csv.DictWriter(out,list(rows[0]),delimiter='\t',lineterminator='\n');w.writeheader();w.writerows(rows)
|
||
(LIVE/'02_资产/资产制作总表.tsv').write_text(out.getvalue(),encoding='utf8')
|
||
report={'total':len(assets),'generated':done,'remaining':len(assets)-done,'characters':sum(a['kind']=='character' for a in assets),'environments':sum(a['kind']=='environment' for a in assets),'missing':[a['id'] for a in assets if not (receipts.get(a['id']) and (LIVE/receipts[a['id']]['path']).is_file())]}
|
||
(LIVE/'05_检查与交付/图像生成进度.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n',encoding='utf8')
|
||
print(json.dumps({k:v for k,v in report.items() if k!='missing'},ensure_ascii=False))
|