// ==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();
})();