Files
kokken/scripts/generate-images.mjs

242 lines
7.5 KiB
JavaScript

import OpenAI from 'openai';
import fs from 'fs/promises';
import path from 'path';
import dotenv from 'dotenv';
import crypto from 'crypto';
dotenv.config();
// Helper til at generere en konsistent hash af en prompt
function hashPrompt(prompt) {
return crypto.createHash('md5').update(prompt).digest('hex');
}
// Cache struktur til at gemme prompts og deres resultater
async function loadCache() {
try {
const cache = await fs.readFile('scripts/image-cache.json', 'utf8');
return JSON.parse(cache);
} catch {
return {};
}
}
async function saveCache(cache) {
await fs.writeFile('scripts/image-cache.json', JSON.stringify(cache, null, 2));
}
const openai = new OpenAI({
apiKey: process.env.OPENAI_KEY
});
const prompts = [
{
name: 'klassisk',
prompt: `High-end food photography of an elegant plated dish:
- Hot-smoked salmon with cream cheese, fresh dill, and thinly sliced radishes
- Beautifully arranged on a modern white ceramic plate
- Natural, directional lighting emphasizing textures
- Shallow depth of field focusing on the salmon
- Professional restaurant-quality presentation
- Garnished with micro herbs and edible flowers
- 4K quality, shot with a high-end camera`
},
{
name: 'veg',
prompt: `Professional food photography of a colorful vegetarian main course:
- Grilled asparagus arranged artfully
- Creamy mushroom risotto with parmesan
- Edible flowers and micro greens as garnish
- Shot from above at a slight angle
- Natural window lighting
- White ceramic plate on a light wooden surface
- Vibrant colors and appealing textures
- High-resolution, restaurant quality presentation`
},
{
name: 'grill',
prompt: `Dramatic food photography of grilled dishes:
- Perfectly grilled medium-rare flank steak with visible grill marks
- Colorful grilled vegetables alongside
- Visible steam or smoke
- Rich, warm lighting emphasizing caramelization
- Shot at a 45-degree angle
- Dark moody background
- Professional food styling with herbs and sea salt
- High-end restaurant presentation quality`
},
{
name: 'nordisk',
prompt: `Nordic cuisine presentation:
- Modern interpretation of traditional Nordic dishes
- Cold-smoked salmon and pickled herring
- Fresh rye bread and traditional accompaniments
- Clean, minimal styling on matte black plates
- Bright natural lighting
- Garnished with fresh dill and edible flowers
- Shot from above with some height
- Professional restaurant quality presentation`
},
{
name: 'tapas',
prompt: `Spanish tapas spread photography:
- Beautifully arranged selection of Spanish tapas
- Serrano ham, manchego cheese, and olives
- Patatas bravas with vibrant sauce
- Traditional ceramic plates and wooden boards
- Warm, Mediterranean-inspired lighting
- Garnished with fresh herbs and olive oil
- Rustic yet elegant presentation
- Shot from a 45-degree angle`
},
{
name: 'brunch',
prompt: `Luxury brunch setting photography:
- Elegant brunch spread with fresh pastries
- Perfectly cooked scrambled eggs with herbs
- Fresh fruits and berries
- Artisanal bread selection
- Bright, morning lighting
- White linens and premium tableware
- Fresh flowers as decoration
- High-end restaurant quality presentation`
},
{
name: 'italiensk',
prompt: `Italian cuisine photography:
- Authentic Italian antipasti spread
- Fresh bruschetta with vibrant tomatoes
- Selection of Italian cured meats
- Artfully arranged on rustic wooden board
- Warm, directional lighting
- Fresh basil and olive oil garnish
- Rustic Italian restaurant atmosphere
- Professional food styling`
},
{
name: 'gourmet',
prompt: `High-end gourmet dish photography:
- Elegant beef tenderloin dish with truffle
- Modern plating on dark ceramic plate
- Artistic sauce presentation
- Micro herbs and edible flowers
- Dramatic lighting emphasizing textures
- Shallow depth of field
- Michelin-star quality presentation
- Shot with professional camera setup`
},
{
name: 'fransk',
prompt: `French haute cuisine photography:
- Elegant plating of foie gras and brioche
- Duck confit with pommes sarladaises
- Sophisticated sauce work and garnishes
- Premium porcelain plate with gold rim
- Professional studio lighting
- Shallow depth of field
- Michelin-star presentation style
- Garnished with edible flowers and micro herbs`
},
{
name: 'jul',
prompt: `Nordic Christmas dinner photography:
- Elegant presentation of traditional Danish Christmas dishes
- Beautifully arranged confited duck leg
- Caramelized potatoes and red cabbage
- Classic white porcelain plate
- Warm, festive lighting with candle glow
- Garnished with fresh herbs
- Holiday atmosphere but modern presentation
- Professional food photography style`
},
{
name: 'fusion',
prompt: `Modern Asian fusion food photography:
- Contemporary Asian fusion dish
- Korean style short ribs with modern plating
- Colorful Asian vegetables and garnishes
- Dramatic black ceramic plate
- Professional studio lighting
- Garnished with micro herbs and sesame
- Artistic sauce presentation
- High-end restaurant quality styling`
}
];async function generateImages() {
const cache = await loadCache();
for (const { name, prompt } of prompts) {
const promptHash = hashPrompt(prompt);
const imagePath = path.join(process.cwd(), 'public', 'img', `${name}.jpg`);
try {
// Tjek om billedet allerede eksisterer
try {
await fs.access(imagePath);
// Hvis billedet findes og prompten er cached, spring over
if (cache[promptHash]) {
console.log(`✓ Skipping ${name}.jpg (already exists with same prompt)`);
continue;
}
} catch {
// Billedet findes ikke, vi skal generere det
}
console.log(`Generating image for ${name}...`);
const response = await openai.images.generate({
model: "dall-e-3",
prompt: prompt,
n: 1,
size: "1024x1024",
quality: "hd",
style: "natural"
});
const imageUrl = response.data[0].url;
// Download billedet
const imageResponse = await fetch(imageUrl);
const buffer = await imageResponse.arrayBuffer();
// Gem billedet
await fs.mkdir(path.join(process.cwd(), 'public', 'img'), { recursive: true });
await fs.writeFile(imagePath, Buffer.from(buffer));
// Gem prompt i cache
cache[promptHash] = {
name,
timestamp: new Date().toISOString()
};
await saveCache(cache);
console.log(`✅ Generated and saved ${name}.jpg`);
} catch (error) {
console.error(`❌ Error generating ${name}:`, error);
}
}
}
// Håndter afbrydelse af scriptet
async function cleanup() {
console.log('\nSaving cache and cleaning up...');
try {
await saveCache(await loadCache());
console.log('Cache saved successfully');
} catch (error) {
console.error('Error saving cache:', error);
}
process.exit(0);
}
// Lyt efter SIGINT (Ctrl+C)
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
// Run the script
generateImages().then(() => {
console.log('All done!');
process.exit(0);
}).catch(error => {
console.error('Error:', error);
process.exit(1);
});