← back to Grassclothwallpaper

force-crop-all-grs.js

203 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 FORCED 20% top crop - NO CONDITIONS
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();

        // ALWAYS 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 forceCropAllGRS() {
    console.log('=== FORCE CROPPING ALL GRS PRODUCTS - NO CONDITIONS ===\n');
    console.log('WILL UPDATE EVERY SINGLE GRS PRODUCT WITH 20% TOP CROP\n');
    console.log('=' .repeat(60) + '\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\n`);
        console.log('STARTING FORCED UPDATE OF ALL PRODUCTS...\n');

        let processedCount = 0;
        let updatedCount = 0;
        let errorCount = 0;

        for (const product of products) {
            processedCount++;

            if (!product.image_url) {
                console.log(`[${processedCount}/${products.length}] ${product.sku}: NO IMAGE`);
                errorCount++;
                continue;
            }

            process.stdout.write(`[${processedCount}/${products.length}] ${product.sku}: `);
            process.stdout.write(`OLD: ${product.hex_color}|${product.hex2_color} → `);

            const newColors = await extractColorsWithCrop(product.image_url);

            if (newColors) {
                process.stdout.write(`NEW: ${newColors.primary}|${newColors.secondary} `);

                // ALWAYS UPDATE - NO COMPARISON
                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');
                    updatedCount++;
                } else {
                    console.log('✗ FAILED');
                    errorCount++;
                }
            } else {
                console.log('ERROR EXTRACTING');
                errorCount++;
            }

            // Progress every 10 items
            if (processedCount % 10 === 0) {
                const percent = Math.round((processedCount / products.length) * 100);
                console.log(`\n--- ${percent}% COMPLETE | ${updatedCount} UPDATED, ${errorCount} ERRORS ---\n`);
            }
        }

        // Final summary
        console.log('\n' + '=' .repeat(70));
        console.log('FORCED CROP COMPLETE');
        console.log('=' .repeat(70));
        console.log(`Total GRS products: ${processedCount}`);
        console.log(`Successfully updated: ${updatedCount}`);
        console.log(`Errors: ${errorCount}`);
        console.log(`\nALL PRODUCTS HAVE BEEN PROCESSED WITH 20% TOP CROP`);

    } catch (error) {
        console.error('Fatal error:', error.message);
    }
}

// RUN IT
forceCropAllGRS();