koboprice/lib/extractPrice.js

75 lines
1.6 KiB
JavaScript

const timeout = (duration) => new Promise((r) => setTimeout(r, duration));
// Selectors for both old and new Kobo layouts
const PRICE_SELECTORS = [
// Old layout (pre-2025 redesign)
".primary-right-container .pricing-details .active-price span",
// New layout (Tailwind, data-testid based)
'[data-testid="price"] .text-title-m-bold',
];
const findPrice = (page) => {
for (const sel of PRICE_SELECTORS) {
const el = page?.querySelector(sel);
if (el?.textContent?.trim()) {
return el.textContent.trim();
}
}
return null;
};
const observePriceOnPage = (page) =>
new Promise((res, rej) => {
timeout(5000).then(() => rej("price not found"));
// Check immediately first
const immediate = findPrice(page);
if (immediate) {
l("found price (immediate)", immediate);
res(immediate);
return;
}
const observer = new MutationObserver(() => {
const price = findPrice(page);
if (price) {
l("found price", price);
observer.disconnect();
res(price);
}
});
observer.observe(page, {
childList: true,
characterData: true,
subtree: true,
});
});
export const extractPrice = async (url) => {
try {
l("going to", url);
const iframe = document.createElement("iframe");
iframe.src = url;
iframe.hidden = true;
document.body.append(iframe);
await new Promise((res) => (iframe.contentWindow.onload = res));
l("starting observing price on", url);
const price = await observePriceOnPage(iframe.contentDocument.body);
document.body.removeChild(iframe);
return price;
} catch (e) {
l("getPriceForCountry", e, url);
return "";
}
};