← back to Grassclothwallpaper
crop-xgr-6201-bottom.js
189 lines
const fetch = require('node-fetch');
const getColors = require('get-image-colors');
const axios = require('axios');
const sharp = require('sharp');
const SUPABASE_URL = 'https://fypvpcxyfmmdhtklgbrt.supabase.co';
const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ5cHZwY3h5Zm1tZGh0a2xnYnJ0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTg4MTAxMjMsImV4cCI6MjA3NDM4NjEyM30.fzDf6VE7jyrqLIbebAwI1NHXi50pI0b_FgQVO-6QcAk';
// Convert color to hex
function toHex(color) {
const rgb = color.rgb();
const r = Math.round(rgb[0]).toString(16).padStart(2, '0');
const g = Math.round(rgb[1]).toString(16).padStart(2, '0');
const b = Math.round(rgb[2]).toString(16).padStart(2, '0');
return `#${r}${g}${b}`.toUpperCase();
}
// Extract colors EXCLUDING bottom 20% (opposite of usual top crop)
async function extractColorsExcludingBottom(imageUrl) {
try {
console.log(` Downloading image: ${imageUrl}`);
const response = await axios.get(imageUrl, {
responseType: 'arraybuffer',
timeout: 20000,
maxContentLength: 15 * 1024 * 1024
});
const buffer = Buffer.from(response.data);
const metadata = await sharp(buffer).metadata();
// EXCLUDE bottom 20% from color extraction
const cropBottom = Math.floor(metadata.height * 0.20);
const usableHeight = metadata.height - cropBottom;
console.log(` Original: ${metadata.width}x${metadata.height}`);
console.log(` Excluding bottom ${cropBottom}px (20%)`);
console.log(` Using top ${usableHeight}px for colors`);
const croppedBuffer = await sharp(buffer)
.extract({
left: 0,
top: 0, // Start from top
width: metadata.width,
height: usableHeight // Use only top 80%
})
.jpeg()
.toBuffer();
const colors = await getColors(croppedBuffer, {
count: 40,
type: 'image/jpeg'
});
const hexColors = colors
.map(c => toHex(c))
.filter(hex => {
const r = parseInt(hex.substr(1, 2), 16);
const g = parseInt(hex.substr(3, 2), 16);
const b = parseInt(hex.substr(5, 2), 16);
const brightness = (r + g + b) / 3;
// Filter out very bright (white text) and very dark colors
return brightness > 15 && brightness < 240;
});
if (hexColors.length < 2) {
const allHex = colors.map(c => toHex(c));
return { primary: allHex[0] || '#808080', secondary: allHex[1] || '#A0A0A0' };
}
// Find most distinct colors
let color1 = hexColors[0];
let color2 = hexColors[1];
let maxDist = 0;
for (let i = 0; i < Math.min(hexColors.length, 20); i++) {
for (let j = i + 1; j < Math.min(hexColors.length, 20); j++) {
const r1 = parseInt(hexColors[i].substr(1, 2), 16);
const g1 = parseInt(hexColors[i].substr(3, 2), 16);
const b1 = parseInt(hexColors[i].substr(5, 2), 16);
const r2 = parseInt(hexColors[j].substr(1, 2), 16);
const g2 = parseInt(hexColors[j].substr(3, 2), 16);
const b2 = parseInt(hexColors[j].substr(5, 2), 16);
const dist = Math.sqrt(
Math.pow(r2 - r1, 2) +
Math.pow(g2 - g1, 2) +
Math.pow(b2 - b1, 2)
);
if (dist > maxDist) {
maxDist = dist;
color1 = hexColors[i];
color2 = hexColors[j];
}
}
}
return { primary: color1, secondary: color2 };
} catch (error) {
console.log(` Error: ${error.message}`);
return null;
}
}
async function cropXGR6201Bottom() {
console.log('🔽 CROPPING BOTTOM 20% OF XGR-6201\n');
console.log('This product needs bottom cropping instead of top cropping');
console.log('=' .repeat(60) + '\n');
try {
// Get XGR-6201 product
const response = await fetch(
`${SUPABASE_URL}/rest/v1/grasscloth_products?sku=eq.XGR-6201&select=*`,
{
headers: {
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`
}
}
);
const products = await response.json();
if (!products || products.length === 0) {
console.log('❌ XGR-6201 not found in database');
return;
}
const product = products[0];
console.log(`\nProcessing XGR-6201:${product.name}`);
console.log(` Current colors: ${product.hex_color} | ${product.hex2_color}`);
if (!product.image_url) {
console.log(' NO IMAGE URL');
return;
}
const newColors = await extractColorsExcludingBottom(product.image_url);
if (newColors) {
console.log(` New colors (excluding bottom 20%): ${newColors.primary} | ${newColors.secondary}`);
// Check brightness
const r = parseInt(newColors.primary.substr(1, 2), 16);
const g = parseInt(newColors.primary.substr(3, 2), 16);
const b = parseInt(newColors.primary.substr(5, 2), 16);
const brightness = Math.round((r + g + b) / 3);
console.log(` Primary brightness: ${brightness}`);
// Update database
const updateResponse = await fetch(
`${SUPABASE_URL}/rest/v1/grasscloth_products?id=eq.${product.id}`,
{
method: 'PATCH',
headers: {
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`,
'Content-Type': 'application/json',
'Prefer': 'return=minimal'
},
body: JSON.stringify({
hex_color: newColors.primary,
hex2_color: newColors.secondary
})
}
);
if (updateResponse.ok) {
console.log(' ✓ UPDATED - colors now exclude bottom 20%!');
} else {
console.log(' ✗ Update failed');
console.log(' Response:', await updateResponse.text());
}
} else {
console.log(' ✗ Could not extract colors');
}
console.log('\n' + '=' .repeat(60));
console.log('XGR-6201 BOTTOM CROP COMPLETE');
console.log('Colors extracted from top 80% of image (excluding bottom text/elements)');
} catch (error) {
console.error('Fatal error:', error.message);
}
}
// Run it
cropXGR6201Bottom();