1

tools/add_lego.js: set cover from largest image and omit it from body

This commit is contained in:
2025-09-11 00:12:40 +02:00
parent c17f07d2f9
commit 2ea31fe3a8

View File

@@ -132,6 +132,58 @@ function escapeMarkdownAlt(text) {
.replace(/\]/g, '\\]'); .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) { async function readYamlTitle(yamlPath) {
try { try {
const content = await fs.readFile(yamlPath, 'utf8'); const content = await fs.readFile(yamlPath, 'utf8');
@@ -327,6 +379,23 @@ async function main() {
imageFiles = Array.from(downloadedImages); 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 = ''; let body = '';
if (imageFiles.length > 0) { if (imageFiles.length > 0) {
const ordered = [ const ordered = [
@@ -335,6 +404,7 @@ async function main() {
]; ];
const lines = []; const lines = [];
for (const base of ordered) { for (const base of ordered) {
if (coverFile && base === coverFile) continue;
const nameNoExt = base.replace(/\.[^.]+$/, ''); const nameNoExt = base.replace(/\.[^.]+$/, '');
const yamlPath = path.join(dataImagesDir, `${nameNoExt}.yaml`); const yamlPath = path.join(dataImagesDir, `${nameNoExt}.yaml`);
const altRaw = (await readYamlTitle(yamlPath)) || `${pageTitle}`; const altRaw = (await readYamlTitle(yamlPath)) || `${pageTitle}`;
@@ -345,13 +415,16 @@ async function main() {
body = lines.join('\n') + '\n'; body = lines.join('\n') + '\n';
} }
const coverLine = coverFile ? `cover: images/${coverFile}\n` : '';
const md = `---\n` + const md = `---\n` +
`title: "${pageTitle.replace(/"/g, '\\"')}"\n` + `title: "${pageTitle.replace(/"/g, '\\"')}"\n` +
`date: ${date}\n` + `date: ${date}\n` +
coverLine +
`---\n\n` + `---\n\n` +
body; body;
await fs.writeFile(indexPath, md, 'utf8'); 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 // Report downloaded images that are not referenced in an existing index.md