// ==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 // @downloadURL none // ==/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 = `
Loading logo Checking other Amazon sites...
`; 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(`${result.name} flag ${result.name}`), createCell(`€${result.price.toFixed(2)}`), createCell(result.deliveryPrice ? ` €${result.deliveryPrice.toFixed(2)}` : '-'), createCell(`€${totalPrice.toFixed(2)}`), createCell(difference !== 0 ? `${difference >= 0 ? '+' : ''}€${difference.toFixed(2)} (${differencePercentage}%)` : '-') ); 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 = `Price history for ${asin}`; priceContainer.appendChild(chartContainer); } function createAliExpressLink(title) { const aliexpressContainer = document.createElement('div'); aliexpressContainer.className = 'aliexpress-container'; aliexpressContainer.innerHTML = ` AliExpress Icon Check on AliExpress`; 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 = ` 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(); })();