← back to Designer Wallcoverings
gemini-enrich-arte.js
121 lines
const https = require('https');
const { Client } = require('pg');
const GEMINI_KEY = '${GOOGLE_API_KEY}';
const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent?key=${GEMINI_KEY}`;
const products = [
{ id: 123, sku: '19433', name: 'Horus Desert Sun', img: 'https://edge.arte-international.com/media/4960d9f1-bc0a-4bcb-9a82-c09d9e5e7d98/conversions/EssentialsLuxor_Horus_19436_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 16, sku: '23401', name: 'Amarna Bister', img: 'https://edge.arte-international.com/media/cc56d30c-5e1c-4b22-878e-b437681e6f1b/conversions/Memphis_Amarna_23400_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 15, sku: '23412', name: 'Sakkara Antique Gold', img: 'https://edge.arte-international.com/media/a580214f-25ff-472e-b88c-6a2bd8dfab64/conversions/Memphis_Sakkara_23410_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 172, sku: '23413', name: 'Sakkara Off-white', img: 'https://edge.arte-international.com/media/a580214f-25ff-472e-b88c-6a2bd8dfab64/conversions/Memphis_Sakkara_23410_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 72, sku: '23421', name: 'Ibis Biscuit', img: 'https://edge.arte-international.com/media/2183e2bc-0620-43c4-81ae-6364abed3937/conversions/Memphis_Ibis_23420_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 75, sku: '23422', name: 'Ibis Chestnut', img: 'https://edge.arte-international.com/media/2183e2bc-0620-43c4-81ae-6364abed3937/conversions/Memphis_Ibis_23420_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 78, sku: '23431', name: 'Siwa Bone', img: 'https://edge.arte-international.com/media/428898de-d288-40f3-b463-1714c59de939/conversions/Memphis_Siwa_23432_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 83, sku: '23433', name: 'Siwa Biscuit', img: 'https://edge.arte-international.com/media/428898de-d288-40f3-b463-1714c59de939/conversions/Memphis_Siwa_23432_Roomshot_Web_LR-medium-two-thirds.jpg' },
{ id: 183, sku: '29221', name: 'Galon Grey Linen', img: 'https://edge.arte-international.com/media/8571da7a-7bf9-4abc-adf0-76863cee5413/conversions/Allures_Galon9224_1_Roomshot_Web_LR-medium-two-thirds.jpg' },
];
function downloadImage(url) {
return new Promise((resolve, reject) => {
const get = (u, redirects = 0) => {
if (redirects > 5) return reject(new Error('Too many redirects'));
https.get(u, { headers: { 'User-Agent': 'Mozilla/5.0' } }, res => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return get(res.headers.location, redirects + 1);
}
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
const chunks = [];
res.on('data', c => chunks.push(c));
res.on('end', () => resolve(Buffer.concat(chunks)));
}).on('error', reject);
};
get(url);
});
}
function callGemini(body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body);
const req = https.request(GEMINI_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } }, res => {
let buf = '';
res.on('data', c => buf += c);
res.on('end', () => { try { resolve(JSON.parse(buf)); } catch(e) { reject(new Error(buf.slice(0,500))); } });
});
req.on('error', reject);
req.write(data);
req.end();
});
}
const PROMPT = `You are an interior design wallcovering analyst. Analyze this wallcovering image and return ONLY valid JSON (no markdown, no backticks):
{
"colors": ["<list 3-6 dominant colors you actually see>"],
"backgroundColor": "<the single dominant background color>",
"styles": ["<1-3 design styles>"],
"patterns": ["<1-3 pattern types>"],
"tags": ["<5-10 interior design tags>"],
"description": "<2-sentence commercial description>"
}
The product is: PRODUCT_NAME. Focus on what you actually see.`;
async function analyzeOne(product) {
console.log(` Downloading image...`);
const imgBuf = await downloadImage(product.img);
console.log(` Downloaded ${(imgBuf.length/1024).toFixed(0)}KB, calling Gemini...`);
const b64 = imgBuf.toString('base64');
const body = {
contents: [{
parts: [
{ text: PROMPT.replace('PRODUCT_NAME', product.name) },
{ inlineData: { mimeType: 'image/jpeg', data: b64 } }
]
}],
generationConfig: { temperature: 0.2, thinkingConfig: { thinkingBudget: 0 } }
};
const res = await callGemini(body);
if (res.error) throw new Error(res.error.message);
const text = res.candidates?.[0]?.content?.parts?.[0]?.text || '';
return JSON.parse(text.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim());
}
async function main() {
const client = new Client({ connectionString: (process.env.DATABASE_URL || 'postgresql://dw_admin@127.0.0.1:5432/dw_unified') });
await client.connect();
console.log('Processing 9 Arte products with base64 image download...\n');
let success = 0, failed = 0;
for (const p of products) {
console.log(`[${success+failed+1}/9] ${p.sku} (${p.name})`);
try {
const a = await analyzeOne(p);
const colors = a.colors?.join(', ') || '';
const bg = a.backgroundColor || '';
const styles = a.styles?.join(', ') || '';
const patterns = a.patterns?.join(', ') || '';
const tags = a.tags?.join(', ') || '';
const desc = a.description || '';
await client.query(`UPDATE vendor_catalog SET ai_colors=$1, ai_background_color=$2, ai_styles=$3, ai_patterns=$4, ai_tags=$5, ai_description=$6 WHERE id=$7`,
[colors, bg, styles, patterns, tags, desc, p.id]);
await client.query(`UPDATE arte_catalog SET ai_colors=$1, ai_background_color=$2, ai_styles=$3, ai_patterns=$4, ai_tags=$5, ai_description=$6 WHERE arte_sku=$7`,
[colors, bg, styles, patterns, tags, desc, p.sku]);
console.log(` ✅ Colors: ${colors}`);
success++;
} catch(e) {
console.log(` ❌ ${e.message}`);
failed++;
}
await new Promise(r => setTimeout(r, 300));
}
console.log(`\nDone: ${success} succeeded, ${failed} failed`);
await client.end();
}
main().catch(e => { console.error(e); process.exit(1); });