European Price Checker for Amazon (fr, de, es, it) with AliExpress Integration

Compare product prices on Amazon.fr, Amazon.de, Amazon.es, and Amazon.it to find the best deal. Integrates CamelCamelCamel for price history charts and checks AliExpress with a summary.

目前为 2024-11-22 提交的版本。查看 最新版本

// ==UserScript==
// @name         European Price Checker for Amazon (fr, de, es, it) with AliExpress Integration
// @namespace    http://tampermonkey.net/
// @version      2.53
// @description  Compare product prices on Amazon.fr, Amazon.de, Amazon.es, and Amazon.it to find the best deal. Integrates CamelCamelCamel for price history charts and checks AliExpress with a summary.
// @author       bNj
// @icon         https://i.ibb.co/qrjrcVy/amz-price-checker.png
// @match        https://www.amazon.fr/*
// @match        https://www.amazon.de/*
// @match        https://www.amazon.es/*
// @match        https://www.amazon.it/*
// @grant        GM_xmlhttpRequest
// @connect      amazon.fr
// @connect      amazon.es
// @connect      amazon.it
// @connect      amazon.de
// @connect      summarizer.mon-bnj.workers.dev
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // Configuration
    const ASIN_REGEX = /\/([A-Z0-9]{10})(?:[/?]|$)/;
    const PARTNER_IDS = {
        fr: 'bnjmazon-21',
        es: 'bnjmazon08-21',
        it: 'bnjmazon0d-21',
        de: 'geeksince190d-21'
    };

    const amazonSites = [
        { name: 'Amazon.fr', country: 'fr', flag: 'https://flagcdn.com/w20/fr.png' },
        { name: 'Amazon.es', country: 'es', flag: 'https://flagcdn.com/w20/es.png' },
        { name: 'Amazon.it', country: 'it', flag: 'https://flagcdn.com/w20/it.png' },
        { name: 'Amazon.de', country: 'de', flag: 'https://flagcdn.com/w20/de.png' }
    ];

    let asin, basePrice, selectedTimePeriod = 'all', priceResults = [], requestCount = 0;

    // Entry point
    function main() {
        if (!extractASIN() || !getBasePrice()) return;
        injectStyles();
        createLoadingContainer();
        fetchPricesFromOtherSites();
    }

    function extractASIN() {
        const asinMatch = window.location.href.match(ASIN_REGEX);
        if (!asinMatch) return false;
        asin = asinMatch[1];
        return true;
    }

    function getBasePrice() {
        basePrice = getPriceFromDocument(document);
        return basePrice !== null;
    }

    function injectStyles() {
        const styles = `
        #amazonPriceComparisonContainer {
            margin-top: 20px;
            padding: 10px;
            background-color: #f9f9f9;
            border: 1px solid #ccc;
            border-radius: 8px;
            position: relative;
            font-size: 11px;
            text-align: center; /* Centrer le texte */
        }
        .comparison-row {
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            padding: 5px 0;
            border-bottom: 1px solid #ccc;
        }
        .comparison-row:hover {
            background-color: #f1f1f1;
        }
        .comparison-row.header-row {
            border-bottom: 2px solid #000;
            font-weight: bold;
            pointer-events: none;
        }
        #loadingMessage {
            text-align: center;
            font-weight: bold;
            font-size: 14px;
            display: flex;
            flex-direction: column;
            align-items: center;
            background-clip: text;
            color: transparent;
            background-image: linear-gradient(270deg, black 0%, black 20%, #FF9900 50%, black 80%, black 100%);
            background-size: 200% 100%;
            animation: loadingAnimation 2s linear infinite;
        }
        @keyframes loadingAnimation {
            0% { background-position: 100% 50%; }
            100% { background-position: 0% 50%; }
        }
        .price-difference-positive {
            color: green;
        }
        .price-difference-negative {
            color: red;
        }
        .controls-container {
            text-align: center;
            margin: 10px;
            display: flex;
            justify-content: space-around; /* Aligne les boutons sur la même ligne */
            align-items: center;
        }
        .aliexpress-container {
			margin-top: 20px;
			padding: 5px 10px;
			border: 1px solid #ccc;
			border-radius: 8px;
			text-align: center;
			max-width: 200px;
			margin: 20px auto;
			cursor: pointer;
			background-color: transparent;
			color: #ff5722;
			font-weight: bold;
			display: flex; /* Utilise flexbox */
			align-items: center; /* Aligne l'icône et le texte au centre verticalement */
			justify-content: center; /* Centre horizontalement */
		}

		.aliexpress-icon {
			width: 24px;
			margin-right: 8px;
		}
        .aliexpress-container:hover {
            background-color: #ffe6cc;
        }
        .loading-text {
			background-clip: text;
			color: transparent;
			background-image: linear-gradient(270deg, black 0%, black 20%, #FF9900 50%, black 80%, black 100%);
			background-size: 200% 100%;
			animation: loadingAnimation 2s linear infinite;
		}

		@keyframes loadingAnimation {
			0% { background-position: 100% 50%; }
			100% { background-position: 0% 50%; }
		}
        .footer {
            text-align: right;
            font-size: 0.7em;
            color: #666;
            margin-top: 10px;
        }
        .footer-logo {
            width: 20px;
            height: 20px;
            vertical-align: middle;
            margin-right: 5px;
        }
        .chart-container {
            text-align: center; /* Centrer le graphique */
            margin: 20px 0; /* Espace autour du graphique */
        }
        `;
        const styleSheet = document.createElement('style');
        styleSheet.type = 'text/css';
        styleSheet.innerText = styles;
        document.head.appendChild(styleSheet);
    }

    function createLoadingContainer() {
        const priceElement = document.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice, #priceblock_saleprice');
        if (priceElement && priceElement.parentNode) {
            const container = document.createElement('div');
            container.id = 'amazonPriceComparisonContainer';
            container.innerHTML = `
                <div id="loadingMessage">
                    <img src="https://i.ibb.co/qrjrcVy/amz-price-checker.png" alt="Loading logo" style="width: 50px; height: 50px; margin-bottom: 10px;">
                    Checking other Amazon sites...
                </div>`;
            priceElement.parentNode.appendChild(container);
        }
    }

    function fetchPricesFromOtherSites() {
        amazonSites.forEach(site => {
            const url = `https://www.amazon.${site.country}/dp/${asin}?tag=${PARTNER_IDS[site.country]}`;
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: { 'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'en-US,en;q=0.5' },
                onload: response => handleResponse(site, response),
                onerror: () => handleResponse(site, null)
            });
        });
    }

    function handleResponse(site, response) {
        requestCount++;
        if (response && response.status === 200) {
            const parser = new DOMParser();
            const doc = parser.parseFromString(response.responseText, 'text/html');
            const price = getPriceFromDocument(doc);
            const deliveryPrice = getDeliveryPriceFromDocument(doc);
            if (price !== null) {
                priceResults.push({ ...site, price, deliveryPrice });
            }
        }
        if (requestCount === amazonSites.length) {
            displayAllResults();
        }
    }

    function displayAllResults() {
        const priceContainer = document.querySelector('#amazonPriceComparisonContainer');
        if (!priceContainer) return;
        priceContainer.innerHTML = '';
        createComparisonTable(priceContainer);
        addControls(priceContainer);
        addCamelCamelCamelChart(priceContainer);
        addAliExpressLink(priceContainer);
        addFooter(priceContainer);
    }

    function createComparisonTable(priceContainer) {
        const headerRow = document.createElement('div');
        headerRow.className = 'comparison-row header-row';
        ['Site', 'Price', 'Delivery', 'Total', 'Difference'].forEach(header => {
            const headerCell = createCell(header, true);
            headerRow.appendChild(headerCell);
        });
        priceContainer.appendChild(headerRow);

        priceResults.sort((a, b) => (a.price + a.deliveryPrice) - (b.price + b.deliveryPrice));

        priceResults.forEach(result => {
            const row = document.createElement('div');
            row.className = 'comparison-row';
            row.onclick = () => window.open(`https://www.amazon.${result.country}/dp/${asin}?tag=${PARTNER_IDS[result.country]}`, '_blank');

            const totalPrice = result.price + (result.deliveryPrice || 0);
            const difference = totalPrice - basePrice;
            const differencePercentage = ((difference / basePrice) * 100).toFixed(2);
            const differenceClass = difference < 0 ? 'price-difference-positive' : difference > 0 ? 'price-difference-negative' : '';

            row.append(
                createCell(`<img src="${result.flag}" alt="${result.name} flag" style="vertical-align: middle; margin-right: 5px;"> ${result.name}`),
                createCell(`€${result.price.toFixed(2)}`),
                createCell(result.deliveryPrice ? `<img src="https://img.icons8.com/?size=100&id=12248&format=png&color=000000" width="20"/> €${result.deliveryPrice.toFixed(2)}` : '-'),
                createCell(`€${totalPrice.toFixed(2)}`),
                createCell(difference !== 0 ? `<span class="${differenceClass}">${difference >= 0 ? '+' : ''}€${difference.toFixed(2)} (${differencePercentage}%)</span>` : '-')
            );

            priceContainer.appendChild(row);
        });
    }

    function createCell(content, isHeader = false) {
        const cell = document.createElement('div');
        cell.style.flex = '1';
        cell.style.textAlign = 'center';
        cell.innerHTML = content;
        if (isHeader) {
            cell.style.fontWeight = 'bold';
        }
        return cell;
    }

    function addControls(priceContainer) {
        const controlsContainer = document.createElement('div');
        controlsContainer.className = 'controls-container';
        const timePeriods = [
            { id: 'btn1Month', label: '1 Month', value: '1m' },
            { id: 'btn3Months', label: '3 Months', value: '3m' },
            { id: 'btn6Months', label: '6 Months', value: '6m' },
            { id: 'btn1Year', label: '1 Year', value: '1y' },
            { id: 'btnAll', label: 'All', value: 'all' }
        ];

        timePeriods.forEach(period => {
            const button = document.createElement('button');
            button.id = period.id;
            button.textContent = period.label;
            button.className = `control-button ${period.value === selectedTimePeriod ? 'active' : ''}`;
            button.addEventListener('click', () => {
                selectedTimePeriod = period.value;
                document.querySelectorAll('.control-button').forEach(btn => {
                    btn.classList.remove('active');
                });
                button.classList.add('active');
                updateChartUrl();
            });
            controlsContainer.appendChild(button);
        });

        const checkboxes = [
            { id: 'checkboxAmazon', label: 'Amazon', filename: 'amazon', disabled: true, checked: true },
            { id: 'checkboxNew', label: 'New', filename: 'new', checked: false, checked: true },
            { id: 'checkboxUsed', label: 'Used', filename: 'used', checked: false }
        ];

        checkboxes.forEach(checkbox => {
            const container = document.createElement('div');
            container.className = 'checkbox-container';

            const checkboxElement = document.createElement('input');
            checkboxElement.type = 'checkbox';
            checkboxElement.id = checkbox.id;
            checkboxElement.checked = checkbox.checked;
            if (checkbox.disabled) checkboxElement.disabled = true;

            checkboxElement.addEventListener('change', updateChartUrl);

            const labelElement = document.createElement('label');
            labelElement.htmlFor = checkbox.id;
            labelElement.textContent = checkbox.label;
            labelElement.className = 'checkbox-label';

            container.append(checkboxElement, labelElement);
            controlsContainer.appendChild(container);
        });

        priceContainer.appendChild(controlsContainer);
    }

    function addCamelCamelCamelChart(priceContainer) {
        const chartContainer = document.createElement('div');
        chartContainer.className = 'chart-container';
        const countryCode = getCurrentCountryCode();
        const chartUrl = getCamelChartUrl(countryCode, asin, selectedTimePeriod);
        const camelUrl = `https://${countryCode}.camelcamelcamel.com/product/${asin}`;
        chartContainer.innerHTML = `<a href="${camelUrl}" target="_blank"><img src="${chartUrl}" alt="Price history for ${asin}" class="chart-image"></a>`;
        priceContainer.appendChild(chartContainer);
    }

    function createAliExpressLink(title) {
        const aliexpressContainer = document.createElement('div');
        aliexpressContainer.className = 'aliexpress-container';
        aliexpressContainer.innerHTML = `
        <img src="https://img.icons8.com/color/48/aliexpress.png" alt="AliExpress Icon" class="aliexpress-icon">
        <span class="aliexpress-text">Check on AliExpress</span>`;

        aliexpressContainer.addEventListener('click', () => {
            const loadingIcon = aliexpressContainer.querySelector('.aliexpress-icon');
            const aliexpressText = aliexpressContainer.querySelector('.aliexpress-text');

            // Change text to Loading and apply animation
            aliexpressText.className = 'loading-text';
            aliexpressText.textContent = 'Loading...';

            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://summarizer.mon-bnj.workers.dev/?text=${encodeURIComponent(title)}`,
                onload: function(response) {
                    handleAliExpressResponse(response, aliexpressContainer);
                },
                onerror: function(error) {
                    console.error("Erreur lors de l'appel à l'API Cloudflare:", error);
                    resetAliExpressButton(aliexpressContainer); // Réinitialiser le bouton
                }
            });
        });

        return aliexpressContainer;
    }


    function handleAliExpressResponse(response, aliexpressContainer) {
        try {
            const data = JSON.parse(response.responseText);
            if (data.summary) {
                const summarizedTitle = data.summary;
                const aliExpressSearchUrl = `https://www.aliexpress.com/wholesale?SearchText=${encodeURIComponent(summarizedTitle)}`;
                resetAliExpressButton(aliexpressContainer);
                setTimeout(() => {
                    window.open(aliExpressSearchUrl, '_blank'); // Redirection dans un nouvel onglet
                }, 100);
            } else {
                throw new Error('Résumé manquant dans la réponse');
            }
        } catch (error) {
            console.error("Erreur lors du traitement de la réponse de l'API Cloudflare:", error);
            resetAliExpressButton(aliexpressContainer); // Réinitialiser le bouton
        }
    }

    function addAliExpressLink(priceContainer) {
        const titleElement = document.querySelector('#productTitle');
        const productTitle = titleElement ? titleElement.textContent.trim() : null;
        if (!productTitle) return;
        const aliexpressContainer = createAliExpressLink(productTitle);
        priceContainer.appendChild(aliexpressContainer);
    }

    function resetAliExpressButton(container) {
    const icon = container.querySelector('.aliexpress-icon');
    const textElement = document.createElement('span');
    textElement.className = 'aliexpress-text';
    textElement.textContent = 'Check on AliExpress';

    container.innerHTML = ''; // Réinitialiser le contenu
    container.appendChild(icon);
    container.appendChild(textElement);
}


    function addFooter(container) {
        const footer = document.createElement('div');
        footer.className = 'footer';
        footer.innerHTML = `
            <img src="https://i.ibb.co/qrjrcVy/amz-price-checker.png" alt="Logo" class="footer-logo">
            European Price Checker for Amazon by bNj v${GM_info.script.version}
        `;
        container.appendChild(footer);
    }

    function getCurrentCountryCode() {
        const hostname = window.location.hostname;
        if (hostname.includes('amazon.de')) return 'de';
        if (hostname.includes('amazon.es')) return 'es';
        if (hostname.includes('amazon.it')) return 'it';
        return 'fr';
    }

    function getCamelChartUrl(countryCode, asin, timePeriod) {
        const selectedFilenames = getSelectedFilenames();
        return `https://charts.camelcamelcamel.com/${countryCode}/${asin}/${selectedFilenames}.png?force=1&zero=0&w=600&h=300&desired=false&legend=1&ilt=1&tp=${timePeriod}&fo=0&lang=en`;
    }

    function getSelectedFilenames() {
        const checkboxes = [
            { id: 'checkboxAmazon', filename: 'amazon' },
            { id: 'checkboxNew', filename: 'new' },
            { id: 'checkboxUsed', filename: 'used' }
        ];
        return Array.from(document.querySelectorAll('input[type="checkbox"]:checked'))
            .map(checkbox => checkboxes.find(cb => cb.id === checkbox.id)?.filename)
            .filter(Boolean)
            .join('-');
    }

    function updateChartUrl() {
        const countryCode = getCurrentCountryCode();
        const chartUrl = getCamelChartUrl(countryCode, asin, selectedTimePeriod);
        const camelUrl = `https://${countryCode}.camelcamelcamel.com/product/${asin}`;
        const chartImage = document.querySelector('#amazonPriceComparisonContainer img[alt^="Price history"]');
        if (chartImage) {
            chartImage.src = chartUrl;
            chartImage.parentElement.href = camelUrl;
        }
    }

    function getPriceFromDocument(doc) {
        const priceElement = doc.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice, #priceblock_saleprice');
        if (!priceElement) return null;
        const priceText = priceElement.textContent;
        return parsePrice(priceText);
    }

    function parsePrice(priceText) {
        if (!priceText) return null;
        const cleanedText = priceText.replace(/[^0-9,\.]/g, '').replace(',', '.');
        const price = parseFloat(cleanedText);
        return isNaN(price) ? null : price;
    }

    function getDeliveryPriceFromDocument(doc) {
        const deliveryMatch = doc.body.innerHTML.match(/data-csa-c-delivery-price="[^"]*?(\d+[.,]\d{2})/);
        if (deliveryMatch) {
            const priceStr = deliveryMatch[1].replace(',', '.');
            const price = parseFloat(priceStr);
            return isNaN(price) ? 0 : price;
        }
        return 0;
    }

    // Start the script
    main();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址