您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Compare product prices on various Amazon sites (FR, DE, ES, IT, COM.BE) to find the best deal. Integrates CamelCamelCamel for price history charts and checks AliExpress with a summary.
当前为
// ==UserScript== // @name European Price Checker for Amazon (fr, de, es, it, com.be) with AliExpress Integration // @namespace http://tampermonkey.net/ // @version 2.6 // @description Compare product prices on various Amazon sites (FR, DE, ES, IT, COM.BE) 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/* // @match https://www.amazon.com.be/* // @grant GM_xmlhttpRequest // @connect amazon.fr // @connect amazon.es // @connect amazon.it // @connect amazon.de // @connect amazon.com.be // @connect summarizer.mon-bnj.workers.dev // @license MIT // ==/UserScript== (function () { 'use strict'; const ASIN_REGEX = /\/([A-Z0-9]{10})(?:[/?]|$)/; const PARTNER_IDS = { fr: 'bnjmazon-21', es: 'bnjmazon08-21', it: 'bnjmazon0d-21', de: 'geeksince190d-21', 'com.be': 'geeksince1900' }; 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' }, { name: 'Amazon.com.be', country: 'com.be', flag: 'https://flagcdn.com/w20/be.png' } ]; let asin, basePrice, selectedTimePeriod = 'all', priceResults = [], requestCount = 0; 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; } .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; 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; align-items: center; justify-content: center; } .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; } .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 */ } .loader { position: relative; width: 48px; height: 48px; border-radius: 50%; display: inline-block; border-top: 4px solid #FFF; border-right: 4px solid transparent; box-sizing: border-box; animation: rotation 1s linear infinite; } .loader::after { content: ''; box-sizing: border-box; position: absolute; left: 0; top: 0; width: 48px; height: 48px; border-radius: 50%; border-left: 4px solid #FF3D00; border-bottom: 4px solid transparent; animation: rotation 0.5s linear infinite reverse; } @keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `; 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: 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}`; const spinner = document.createElement('div'); spinner.className = 'loader'; const chartImage = document.createElement('img'); chartImage.alt = `Price history for ${asin}`; chartImage.className = 'chart-image'; chartImage.style.display = 'none'; chartImage.addEventListener('load', () => { spinner.style.display = 'none'; chartImage.style.display = 'block'; }); chartImage.addEventListener('error', () => { spinner.style.display = 'none'; chartImage.style.display = 'block'; chartImage.src = 'https://via.placeholder.com/600x300?text=Image+Unavailable'; }); chartImage.src = chartUrl; // Lien proxifié const anchor = document.createElement('a'); anchor.href = camelUrl; anchor.target = '_blank'; anchor.appendChild(chartImage); chartContainer.appendChild(spinner); chartContainer.appendChild(anchor); priceContainer.appendChild(chartContainer); } function getCamelChartUrl(countryCode, asin, timePeriod) { const selectedFilenames = getSelectedFilenames(); const originalCamelUrl = `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`; return `https://camelcamelcamel.mon-bnj.workers.dev/?target=${encodeURIComponent(originalCamelUrl)}`; } 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.chart-image'); if (chartImage) { // On récupère le loader const spinner = chartImage.parentElement.parentElement.querySelector('.loader'); if (spinner) spinner.style.display = 'inline-block'; chartImage.style.display = 'none'; chartImage.src = chartUrl; chartImage.parentElement.href = camelUrl; } } 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'); 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); } }); }); 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'); }, 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); } } 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 = ''; 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'; if (hostname.includes('amazon.com.be')) return 'com.be'; return 'fr'; } 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; } main(); })();