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();