Extended the image optimization script to process both project and blog post images: - Added constants for posts input/output directories - Refactored processImage to accept output directory parameter - Updated processDirectory to handle multiple content types - Main function now processes both projects and posts directories - Gracefully handles missing directories with informative messages
138 lines
4.2 KiB
JavaScript
138 lines
4.2 KiB
JavaScript
import sharp from 'sharp';
|
|
import { readdir, mkdir } from 'fs/promises';
|
|
import { join, dirname, basename, extname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { existsSync } from 'fs';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
// Mehr Größen für bessere Mobile-Unterstützung
|
|
const SIZES = [200, 300, 400, 600, 800, 1200];
|
|
const PROJECTS_INPUT_DIR = join(__dirname, '../source-images/projects');
|
|
const PROJECTS_OUTPUT_DIR = join(__dirname, '../public/images/projects');
|
|
const POSTS_INPUT_DIR = join(__dirname, '../source-images/posts');
|
|
const POSTS_OUTPUT_DIR = join(__dirname, '../public/images/posts');
|
|
|
|
async function processImage(inputPath, slug, outputBaseDir) {
|
|
const filename = basename(inputPath, extname(inputPath));
|
|
const ext = extname(inputPath).toLowerCase();
|
|
|
|
if (!['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) {
|
|
return;
|
|
}
|
|
|
|
const outputDir = join(outputBaseDir, slug);
|
|
await mkdir(outputDir, { recursive: true });
|
|
|
|
console.log(`Processing: ${slug}/${filename}${ext}`);
|
|
|
|
// Optimierte Originalversion (cover.jpg)
|
|
const originalOutputPath = join(outputDir, `${filename}.jpg`);
|
|
try {
|
|
await sharp(inputPath)
|
|
.jpeg({ quality: 85, progressive: true })
|
|
.toFile(originalOutputPath);
|
|
console.log(` Created: ${filename}.jpg (optimized original)`);
|
|
} catch (error) {
|
|
console.error(` Error creating optimized original:`, error.message);
|
|
}
|
|
|
|
// JPG-Versionen in verschiedenen Größen
|
|
for (const width of SIZES) {
|
|
const outputFilename = `${filename}-${width}w.jpg`;
|
|
const outputPath = join(outputDir, outputFilename);
|
|
|
|
try {
|
|
await sharp(inputPath)
|
|
.resize(width, null, {
|
|
withoutEnlargement: true,
|
|
fit: 'inside'
|
|
})
|
|
.jpeg({ quality: 80, progressive: true })
|
|
.toFile(outputPath);
|
|
|
|
console.log(` Created: ${outputFilename}`);
|
|
} catch (error) {
|
|
console.error(` Error creating ${outputFilename}:`, error.message);
|
|
}
|
|
}
|
|
|
|
// WebP-Versionen in verschiedenen Größen
|
|
for (const width of SIZES) {
|
|
const webpFilename = `${filename}-${width}w.webp`;
|
|
const webpPath = join(outputDir, webpFilename);
|
|
|
|
try {
|
|
await sharp(inputPath)
|
|
.resize(width, null, {
|
|
withoutEnlargement: true,
|
|
fit: 'inside'
|
|
})
|
|
.webp({ quality: 75 })
|
|
.toFile(webpPath);
|
|
|
|
console.log(` Created: ${webpFilename}`);
|
|
} catch (error) {
|
|
console.error(` Error creating ${webpFilename}:`, error.message);
|
|
}
|
|
}
|
|
|
|
// Volle WebP-Version
|
|
const webpFullPath = join(outputDir, `${filename}.webp`);
|
|
try {
|
|
await sharp(inputPath)
|
|
.webp({ quality: 80 })
|
|
.toFile(webpFullPath);
|
|
console.log(` Created: ${filename}.webp`);
|
|
} catch (error) {
|
|
console.error(` Error creating full WebP:`, error.message);
|
|
}
|
|
}
|
|
|
|
async function processDirectory(inputDir, outputDir, typeName) {
|
|
if (!existsSync(inputDir)) {
|
|
console.log(`Skipping ${typeName}: Source directory not found: ${inputDir}`);
|
|
return;
|
|
}
|
|
|
|
const entries = await readdir(inputDir, { withFileTypes: true });
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = join(inputDir, entry.name);
|
|
|
|
if (entry.isDirectory()) {
|
|
const coverPath = join(fullPath, 'cover.jpg');
|
|
if (existsSync(coverPath)) {
|
|
await processImage(coverPath, entry.name, outputDir);
|
|
} else {
|
|
console.log(`Skipping ${entry.name}: no cover.jpg found`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log('Starting image optimization...');
|
|
console.log(`Sizes: ${SIZES.join(', ')}px\n`);
|
|
|
|
try {
|
|
console.log('Processing projects...');
|
|
console.log(`Source: ${PROJECTS_INPUT_DIR}`);
|
|
console.log(`Output: ${PROJECTS_OUTPUT_DIR}\n`);
|
|
await processDirectory(PROJECTS_INPUT_DIR, PROJECTS_OUTPUT_DIR, 'projects');
|
|
|
|
console.log('\nProcessing posts...');
|
|
console.log(`Source: ${POSTS_INPUT_DIR}`);
|
|
console.log(`Output: ${POSTS_OUTPUT_DIR}\n`);
|
|
await processDirectory(POSTS_INPUT_DIR, POSTS_OUTPUT_DIR, 'posts');
|
|
|
|
console.log('\nImage optimization complete!');
|
|
} catch (error) {
|
|
console.error('Error during optimization:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main();
|