← back to Grassclothwallpaper
process-grs-20385-first.js
293 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 with 20% top crop
async function extractColorsWithCrop(imageUrl) {
try {
const response = await axios.get(imageUrl, {
responseType: 'arraybuffer',
timeout: 15000,
maxContentLength: 10 * 1024 * 1024
});
const buffer = Buffer.from(response.data);
const metadata = await sharp(buffer).metadata();
// Crop 20% from top, 15% from bottom
const cropTop = Math.floor(metadata.height * 0.2);
const cropBottom = Math.floor(metadata.height * 0.15);
const croppedHeight = metadata.height - cropTop - cropBottom;
const croppedBuffer = await sharp(buffer)
.extract({
left: 0,
top: cropTop,
width: metadata.width,
height: croppedHeight
})
.jpeg()
.toBuffer();
const colors = await getColors(croppedBuffer, {
count: 30,
type: 'image/jpeg'
});
// Filter out whites and blacks
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;
return brightness > 20 && brightness < 235;
});
if (hexColors.length < 2) return null;
// Find most distinct colors
let color1 = hexColors[0];
let color2 = hexColors[1];
let maxDist = 0;
for (let i = 0; i < Math.min(hexColors.length, 15); i++) {
for (let j = i + 1; j < Math.min(hexColors.length, 15); 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) {
return null;
}
}
async function processGRS20385() {
console.log('=== PROCESSING GRS-20385 FIRST ===\n');
try {
// Get GRS-20385 product
const response = await fetch(
`${SUPABASE_URL}/rest/v1/grasscloth_products?sku=eq.GRS-20385&select=*`,
{
headers: {
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`
}
}
);
const products = await response.json();
if (!products || products.length === 0) {
console.log('GRS-20385 not found in database');
return;
}
const product = products[0];
console.log('Found GRS-20385:');
console.log(` Title: ${product.title || 'No title'}`);
console.log(` Current colors: ${product.hex_color} | ${product.hex2_color}`);
console.log(` Image URL: ${product.image_url}\n`);
if (!product.image_url) {
console.log('No image URL for GRS-20385');
return;
}
console.log('Applying 20% top crop, 15% bottom crop...\n');
const newColors = await extractColorsWithCrop(product.image_url);
if (newColors) {
console.log(`New colors: ${newColors.primary} | ${newColors.secondary}\n`);
// Check if colors actually changed
if (newColors.primary === product.hex_color && newColors.secondary === product.hex2_color) {
console.log('No change needed - colors are the same');
} else {
console.log('Updating database...');
// 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('✓ GRS-20385 successfully updated!');
console.log(` Old: ${product.hex_color} | ${product.hex2_color}`);
console.log(` New: ${newColors.primary} | ${newColors.secondary}`);
} else {
console.log('✗ Failed to update GRS-20385');
}
}
} else {
console.log('Could not extract colors from GRS-20385');
}
console.log('\n=== GRS-20385 PROCESSING COMPLETE ===\n');
// Now continue with all GRS products
console.log('Continuing with all other GRS products...\n');
await processAllGRSProducts();
} catch (error) {
console.error('Error processing GRS-20385:', error.message);
}
}
async function processAllGRSProducts() {
console.log('=== PROCESSING ALL GRS PRODUCTS ===\n');
try {
// Get ALL GRS products
const response = await fetch(
`${SUPABASE_URL}/rest/v1/grasscloth_products?select=*&sku=like.GRS*&order=sku`,
{
headers: {
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`
}
}
);
if (!response.ok) {
console.error(`Failed to fetch products: ${response.status} ${response.statusText}`);
return;
}
const products = await response.json();
if (!Array.isArray(products)) {
console.error('Unexpected response from Supabase:', products);
return;
}
console.log(`Found ${products.length} GRS products total\n`);
let processedCount = 0;
let fixedCount = 0;
let skippedCount = 0;
let errorCount = 0;
for (const product of products) {
processedCount++;
// Skip if no image
if (!product.image_url) {
console.log(`[${processedCount}/${products.length}] ${product.sku}: NO IMAGE`);
errorCount++;
continue;
}
// Show progress
process.stdout.write(`[${processedCount}/${products.length}] ${product.sku}: `);
const newColors = await extractColorsWithCrop(product.image_url);
if (newColors) {
// Check if colors changed
if (newColors.primary === product.hex_color && newColors.secondary === product.hex2_color) {
console.log('no change');
skippedCount++;
} else {
// 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(`✓ ${product.hex_color} → ${newColors.primary}`);
fixedCount++;
} else {
console.log('✗ update failed');
errorCount++;
}
}
} else {
console.log('FAILED');
errorCount++;
}
// Progress summary every 20 items
if (processedCount % 20 === 0) {
const percentComplete = Math.round((processedCount / products.length) * 100);
console.log(`\n--- ${percentComplete}% complete | ${fixedCount} updated, ${skippedCount} unchanged ---\n`);
}
}
// Final summary
console.log('\n' + '=' .repeat(70));
console.log('COMPLETE SUMMARY');
console.log('=' .repeat(70));
console.log(`Total GRS products: ${processedCount}`);
console.log(`Updated: ${fixedCount}`);
console.log(`Unchanged: ${skippedCount}`);
console.log(`Errors: ${errorCount}`);
console.log(`Success rate: ${Math.round(((fixedCount + skippedCount) / processedCount) * 100)}%`);
} catch (error) {
console.error('Error processing all GRS products:', error.message);
}
}
// Run - process GRS-20385 first, then all others
processGRS20385();