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