tools/add_lego.js: set cover from largest image and omit it from body
This commit is contained in:
@@ -132,6 +132,58 @@ function escapeMarkdownAlt(text) {
|
||||
.replace(/\]/g, '\\]');
|
||||
}
|
||||
|
||||
// Lightweight dimensions readers for PNG/JPEG/GIF. Fallback to file size.
|
||||
async function getImageDimensions(filePath) {
|
||||
const fd = await fs.open(filePath, 'r');
|
||||
try {
|
||||
const stat = await fd.stat();
|
||||
const size = stat.size || 0;
|
||||
const hdr = Buffer.alloc(64);
|
||||
const { bytesRead } = await fd.read(hdr, 0, hdr.length, 0);
|
||||
const b = hdr.slice(0, bytesRead);
|
||||
// PNG
|
||||
if (b.length >= 24 && b[0] === 0x89 && b[1] === 0x50 && b[2] === 0x4E && b[3] === 0x47) {
|
||||
const width = b.readUInt32BE(16);
|
||||
const height = b.readUInt32BE(20);
|
||||
return { width, height, area: width * height, size };
|
||||
}
|
||||
// GIF
|
||||
if (b.length >= 10 && b[0] === 0x47 && b[1] === 0x49 && b[2] === 0x46) {
|
||||
const width = b.readUInt16LE(6);
|
||||
const height = b.readUInt16LE(8);
|
||||
return { width, height, area: width * height, size };
|
||||
}
|
||||
// JPEG: scan segments for SOF markers
|
||||
if (b.length >= 4 && b[0] === 0xFF && b[1] === 0xD8) {
|
||||
let pos = 2;
|
||||
while (pos + 3 < size) {
|
||||
const markerBuf = Buffer.alloc(4);
|
||||
await fd.read(markerBuf, 0, 4, pos);
|
||||
if (markerBuf[0] !== 0xFF) { pos += 1; continue; }
|
||||
const marker = markerBuf[1];
|
||||
const hasLen = marker !== 0xD8 && marker !== 0xD9;
|
||||
let segLen = 0;
|
||||
if (hasLen) segLen = markerBuf.readUInt16BE(2);
|
||||
// SOF0..SOF15 except DHT(0xC4), JPG(0xC8), DAC(0xCC)
|
||||
if (marker >= 0xC0 && marker <= 0xCF && ![0xC4, 0xC8, 0xCC].includes(marker)) {
|
||||
const segHdr = Buffer.alloc(7);
|
||||
await fd.read(segHdr, 0, segHdr.length, pos + 4);
|
||||
const height = segHdr.readUInt16BE(1);
|
||||
const width = segHdr.readUInt16BE(3);
|
||||
return { width, height, area: width * height, size };
|
||||
}
|
||||
if (!hasLen || segLen < 2) { pos += 2; continue; }
|
||||
pos += 2 + segLen;
|
||||
}
|
||||
}
|
||||
return { width: 0, height: 0, area: 0, size };
|
||||
} catch (_) {
|
||||
return { width: 0, height: 0, area: 0, size: 0 };
|
||||
} finally {
|
||||
await fd.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function readYamlTitle(yamlPath) {
|
||||
try {
|
||||
const content = await fs.readFile(yamlPath, 'utf8');
|
||||
@@ -327,6 +379,23 @@ async function main() {
|
||||
imageFiles = Array.from(downloadedImages);
|
||||
}
|
||||
|
||||
// Choose cover: prefer downloaded this run; fallback to all images
|
||||
let coverFile = null;
|
||||
const coverCandidates = (downloadedImages.size > 0)
|
||||
? Array.from(downloadedImages).filter((b) => imageFiles.includes(b))
|
||||
: imageFiles.slice();
|
||||
if (coverCandidates.length > 0) {
|
||||
let best = null;
|
||||
let bestScore = -1;
|
||||
for (const base of coverCandidates) {
|
||||
const full = path.join(imagesDir, base);
|
||||
const dim = await getImageDimensions(full);
|
||||
const score = (dim.area && dim.area > 0) ? dim.area : (dim.size || 0);
|
||||
if (score > bestScore) { bestScore = score; best = base; }
|
||||
}
|
||||
coverFile = best;
|
||||
}
|
||||
|
||||
let body = '';
|
||||
if (imageFiles.length > 0) {
|
||||
const ordered = [
|
||||
@@ -335,6 +404,7 @@ async function main() {
|
||||
];
|
||||
const lines = [];
|
||||
for (const base of ordered) {
|
||||
if (coverFile && base === coverFile) continue;
|
||||
const nameNoExt = base.replace(/\.[^.]+$/, '');
|
||||
const yamlPath = path.join(dataImagesDir, `${nameNoExt}.yaml`);
|
||||
const altRaw = (await readYamlTitle(yamlPath)) || `${pageTitle}`;
|
||||
@@ -345,13 +415,16 @@ async function main() {
|
||||
body = lines.join('\n') + '\n';
|
||||
}
|
||||
|
||||
const coverLine = coverFile ? `cover: images/${coverFile}\n` : '';
|
||||
const md = `---\n` +
|
||||
`title: "${pageTitle.replace(/"/g, '\\"')}"\n` +
|
||||
`date: ${date}\n` +
|
||||
coverLine +
|
||||
`---\n\n` +
|
||||
body;
|
||||
await fs.writeFile(indexPath, md, 'utf8');
|
||||
console.log(`[create] ${path.relative(ROOT, indexPath)} with ${imageFiles.length} image reference(s)`);
|
||||
const coverInfo = coverFile ? `, cover=images/${coverFile}` : '';
|
||||
console.log(`[create] ${path.relative(ROOT, indexPath)} with ${imageFiles.length} image reference(s)${coverInfo}`);
|
||||
}
|
||||
|
||||
// Report downloaded images that are not referenced in an existing index.md
|
||||
|
||||
Reference in New Issue
Block a user