Files
Art-gallery/scripts/regenerate-portrait-thumbs.js
T
Danila Khodjaef 99b8559607 Speed up timeline load with portrait thumbs, bootstrap API, and caching.
Add catalog bootstrap endpoint, portrait thumbnail pipeline, lazy queued timeline images, gzip compression, and 3D texture throttling with code-split VirtualGallery.
2026-07-06 14:27:29 +03:00

77 lines
2.3 KiB
JavaScript

/**
* Generate timeline portrait thumbnails from existing full portraits.
* Writes portraits/thumbs/{Artist}_thumb.jpg and updates portrait_thumb_path.
*/
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const pool = require('../server/db');
const { generateThumbnailFromFull } = require('./image-fetcher');
const IMAGE_DIR = path.resolve(process.env.IMAGE_DIR || './data/images');
const PORTRAIT_THUMB_WIDTH = 256;
function safeArtistPortraitBase(artistName) {
return artistName.replace(/[^a-zA-Z0-9_-]/g, '_');
}
function localPath(rel) {
return path.join(IMAGE_DIR, rel);
}
async function generatePortraitThumb(row) {
if (!row.portrait_path) return { skipped: true, reason: 'no portrait' };
const fullPath = localPath(row.portrait_path);
if (!fs.existsSync(fullPath)) return { skipped: true, reason: 'missing file' };
const safeBase = safeArtistPortraitBase(row.name);
const thumbsDir = path.join(IMAGE_DIR, 'portraits', 'thumbs');
if (!fs.existsSync(thumbsDir)) fs.mkdirSync(thumbsDir, { recursive: true });
const thumbDest = path.join(thumbsDir, `${safeBase}_thumb.jpg`);
await generateThumbnailFromFull(fullPath, thumbDest, PORTRAIT_THUMB_WIDTH);
const portraitThumbPath = path.join('portraits', 'thumbs', `${safeBase}_thumb.jpg`).replace(/\\/g, '/');
await pool.query(`UPDATE artists SET portrait_thumb_path = $1 WHERE id = $2`, [
portraitThumbPath,
row.id,
]);
return { skipped: false, portraitThumbPath };
}
(async () => {
const { rows } = await pool.query(
`SELECT id, name, portrait_path, portrait_thumb_path FROM artists
WHERE portrait_path IS NOT NULL AND portrait_path <> ''
ORDER BY id`
);
let generated = 0;
let skipped = 0;
const errors = [];
for (const row of rows) {
try {
const result = await generatePortraitThumb(row);
if (result.skipped) skipped++;
else generated++;
} catch (err) {
errors.push({ id: row.id, name: row.name, error: err.message });
}
}
console.log(`Portrait thumbs: ${generated} generated, ${skipped} skipped, ${errors.length} errors`);
if (errors.length) {
for (const e of errors.slice(0, 20)) {
console.warn(` #${e.id} ${e.name}: ${e.error}`);
}
}
await pool.end();
})().catch((err) => {
console.error(err);
pool.end().finally(() => process.exit(1));
});