diff --git a/src/stores/gull.js b/src/stores/gull.js index e1a2fa2..c5a9c14 100644 --- a/src/stores/gull.js +++ b/src/stores/gull.js @@ -2,25 +2,57 @@ const { decodeHtml, cleanText, extractFirstImgUrl } = require("../utils/html"); const { normalizeCspc } = require("../utils/sku"); -const { extractPriceFromTmbBlock } = require("../utils/woocommerce"); const { makePageUrl } = require("../utils/url"); function looksInStock(block) { const s = String(block || ""); if (/\boutofstock\b/i.test(s)) return false; - // your sample has:

1 in stock

if (/\bin-stock\b/i.test(s)) return true; if (/\binstock\b/i.test(s)) return true; if (/>\s*\d+\s+in\s+stock\s*= 1.00 (sale price if present). +function extractGullPriceFromBlock(block) { + const s = String(block || ""); + const nums = []; + + // Match WooCommerce "Price amount" blocks, pull out the BDI contents, + // then strip tags/entities and parse as float. + const re = + /]*class=["'][^"']*\bwoocommerce-Price-amount\b[^"']*["'][^>]*>\s*]*>([\s\S]*?)<\/bdi>/gi; + + for (const m of s.matchAll(re)) { + const raw = cleanText(decodeHtml(m[1] || "")); // e.g. "$24.05" + const n = parseFloat(String(raw).replace(/[^0-9.]/g, "")); + if (Number.isFinite(n)) nums.push(n); + } + + // Filter out bottle deposits / tiny fees (usually 0.10, 0.20, etc.) + const big = nums.filter((n) => n >= 1.0); + + if (!big.length) return ""; + + // If sale price exists, Woo often renders old then new; taking the last >=1 + // typically yields the current price. + const chosen = big[big.length - 1]; + + // Normalize formatting + return `$${chosen.toFixed(2)}`; +} + function parseProductsGull(html, ctx) { const s = String(html || ""); const items = []; // split on
  • - const parts = s.split(/]*class=["'][^"']*\bproduct\b[^"']*["'][^>]*>/i); + const parts = s.split( + /]*class=["'][^"']*\bproduct\b[^"']*["'][^>]*>/i + ); if (parts.length <= 1) return items; const base = `https://${(ctx && ctx.store && ctx.store.host) || "gullliquorstore.com"}/`; @@ -30,7 +62,9 @@ function parseProductsGull(html, ctx) { if (!looksInStock(block)) continue; - const hrefM = block.match(/]*href=["']([^"']+)["'][^>]*class=["'][^"']*\bwoocommerce-LoopProduct-link\b/i); + const hrefM = block.match( + /]*href=["']([^"']+)["'][^>]*class=["'][^"']*\bwoocommerce-LoopProduct-link\b/i + ); if (!hrefM || !hrefM[1]) continue; let url; @@ -40,17 +74,18 @@ function parseProductsGull(html, ctx) { continue; } - const titleM = block.match(/]*class=["'][^"']*\bwoocommerce-loop-product__title\b[^"']*["'][^>]*>([\s\S]*?)<\/h2>/i); + const titleM = block.match( + /]*class=["'][^"']*\bwoocommerce-loop-product__title\b[^"']*["'][^>]*>([\s\S]*?)<\/h2>/i + ); const name = cleanText(decodeHtml(titleM ? titleM[1] : "")); if (!name) continue; - // Price is in standard Woo ... - const price = extractPriceFromTmbBlock(block) || ""; + const price = extractGullPriceFromBlock(block); const sku = normalizeCspc( block.match(/\bdata-product_sku=["']([^"']+)["']/i)?.[1] || - block.match(/\bSKU\b[^0-9]{0,20}(\d{6})\b/i)?.[1] || - url + block.match(/\bSKU\b[^0-9]{0,20}(\d{6})\b/i)?.[1] || + url ); const img = extractFirstImgUrl(block, base); @@ -63,6 +98,7 @@ function parseProductsGull(html, ctx) { return [...uniq.values()]; } + function createStore(defaultUa) { return { key: "gull", @@ -72,26 +108,26 @@ function createStore(defaultUa) { parseProducts: parseProductsGull, makePageUrl, // enables /page/N/ paging categories: [ - { - key: "whisky", - label: "Whisky", - startUrl: "https://gullliquorstore.com/product-category/spirits/?spirit_type=whisky", - discoveryStartPage: 3, - discoveryStep: 2, - pageConcurrency: 1, - pageStaggerMs: 10000, - discoveryDelayMs: 10000 - }, - { - key: "rum", - label: "Rum", - startUrl: "https://gullliquorstore.com/product-category/spirits/?spirit_type=rum", - discoveryStartPage: 3, - discoveryStep: 2, - pageConcurrency: 1, - pageStaggerMs: 10000, - discoveryDelayMs: 10000 - }, + { + key: "whisky", + label: "Whisky", + startUrl: "https://gullliquorstore.com/product-category/spirits/?spirit_type=whisky", + discoveryStartPage: 3, + discoveryStep: 2, + pageConcurrency: 1, + pageStaggerMs: 10000, + discoveryDelayMs: 10000, + }, + { + key: "rum", + label: "Rum", + startUrl: "https://gullliquorstore.com/product-category/spirits/?spirit_type=rum", + discoveryStartPage: 3, + discoveryStep: 2, + pageConcurrency: 1, + pageStaggerMs: 10000, + discoveryDelayMs: 10000, + }, ], }; }