diff --git a/scripts/publish-document-covers.mjs b/scripts/publish-document-covers.mjs index 35c255e..06407f9 100644 --- a/scripts/publish-document-covers.mjs +++ b/scripts/publish-document-covers.mjs @@ -1,4 +1,6 @@ /** Run INSIDE the server using auth-cli.cjs run; no content or credentials are exported. */ +import {createRequire} from 'node:module'; +import {createHash} from 'node:crypto'; import {readFile,writeFile,mkdir} from 'node:fs/promises';import path from 'node:path'; const base=process.env.PROMPT_LOCAL_API||'http://127.0.0.1:3000',dir=process.env.PROMPT_COVER_BUNDLE||'/tmp/prompt-document-covers'; const catalog=JSON.parse(await readFile(path.join(dir,'catalog.json'),'utf8')),stateFile=process.env.PROMPT_COVER_STATE||'/tmp/prompt-document-cover-state.json'; @@ -22,5 +24,19 @@ for(const n of catalog.notes){if(state.done.includes(n.id))continue;const entry= state.done.push(n.id);await save(); } for(const n of [...catalog.roots,...catalog.entries.map(id=>({id}))]){const entry=await read(n.id);await snapshot(entry);await api('/api/notes/'+n.id,json('PATCH',{tags:await labels(entry,n.topic?[n.topic]:[],['document-entry']),revision:entry.revision}));} -for(const slug of catalog.references){const r=await fetch(base+'/p/'+slug,{redirect:'manual'});const location=r.headers.get('location');if(!location?.startsWith('/explore/'))throw Error('Reference post unavailable');const entry=await read(location.split('/').at(-1));await snapshot(entry);await api('/api/notes/'+entry.id,json('PATCH',{tags:await labels(entry,['贴纸'],['reference-image']),revision:entry.revision}));} +for(const slug of catalog.references){ + const r=await fetch(base+'/p/'+slug,{redirect:'manual'}),location=r.headers.get('location');if(!location?.startsWith('/explore/'))throw Error('Reference post unavailable'); + const entry=await read(location.split('/').at(-1));await snapshot(entry); + let cover=state.assets['reference-cover:'+slug]; + if(!cover){ + const sharp=createRequire('/app/package.json')('sharp'),source=state.snapshots[entry.id].media.filter(m=>m.kind==='image'),cols=source.length>6?3:2,rows=Math.ceil(source.length/cols),cellW=Math.floor(756/cols),cellH=Math.min(295,Math.floor(815/rows)),parts=[]; + for(let i=0;iPROMPT / REFERENCES${title}${source.length} 张原始图片 · 本页为索引封面参考图 / 贴纸 · 原件在图包中保留`; + const bytes=await sharp({create:{width:900,height:1200,channels:3,background:'#f4f0e6'}}).composite([...parts,{input:Buffer.from(label),left:0,top:0}]).png().toBuffer(); + cover=await upload('reference-cover:'+slug,bytes,slug+'-index.png');state.referenceHashes||={};state.referenceHashes[entry.id]=createHash('sha256').update(bytes).digest('hex');await save(); + } + const assets=[{id:cover,role:'content'},...entry.assets].filter((a,i,arr)=>arr.findIndex(b=>b.id===a.id)===i); + await api('/api/notes/'+entry.id,json('PUT',{title:entry.title,description:entry.description,markdown:entry.editableMarkdown,tags:await labels(entry,['贴纸'],['reference-image']),assets,coverAssetId:cover,revision:entry.revision})); +} console.log(JSON.stringify({independentCovers:catalog.notes.length,rootEntries:catalog.roots.length+catalog.entries.length,referencePosts:catalog.references.length,contentExported:false}));