mirror of
https://github.com/clucraft/PriceGhost.git
synced 2026-05-15 10:52:36 +02:00
Fix stock status false positives for in-stock items
- Remove overly generic pre-order phrases that caused false positives
("available in", "coming in", "arriving in" matched normal text)
- Add in-stock phrase priority check - "in stock", "add to cart",
"add to basket" now take precedence over pre-order detection
- Add Magento 2 stock status detection using stock classes and
add-to-cart buttons
- Bump version to 1.0.2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2c8843ed8a
commit
afa4f0c96a
7 changed files with 89 additions and 26 deletions
|
|
@ -742,9 +742,36 @@ const siteScrapers: SiteScraper[] = [
|
|||
$('.fotorama__stage img').first().attr('src') ||
|
||||
null;
|
||||
|
||||
// Stock status detection for Magento 2
|
||||
let stockStatus: StockStatus = 'unknown';
|
||||
|
||||
// Check for Magento's stock status elements
|
||||
const stockElement = $('.product-info-stock-sku .stock').first();
|
||||
const stockText = stockElement.text().toLowerCase();
|
||||
const stockClass = stockElement.attr('class')?.toLowerCase() || '';
|
||||
|
||||
// Magento uses "available" class for in-stock items
|
||||
if (stockClass.includes('available') || stockText.includes('in stock')) {
|
||||
stockStatus = 'in_stock';
|
||||
} else if (stockClass.includes('unavailable') || stockText.includes('out of stock')) {
|
||||
stockStatus = 'out_of_stock';
|
||||
}
|
||||
|
||||
// Also check for add to cart button as backup
|
||||
if (stockStatus === 'unknown') {
|
||||
const addToCartBtn = $('#product-addtocart-button, button.tocart, button[title="Add to Cart"], button[title="Add to Basket"]').length > 0;
|
||||
const outOfStockMsg = $('.out-of-stock, .unavailable, [class*="outofstock"]').length > 0;
|
||||
|
||||
if (addToCartBtn && !outOfStockMsg) {
|
||||
stockStatus = 'in_stock';
|
||||
} else if (outOfStockMsg) {
|
||||
stockStatus = 'out_of_stock';
|
||||
}
|
||||
}
|
||||
|
||||
// Only return if we found a price (indicates it's likely a Magento site)
|
||||
if (price) {
|
||||
return { name, price, imageUrl };
|
||||
return { name, price, imageUrl, stockStatus };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
|
@ -1259,13 +1286,11 @@ function extractGenericStockStatus($: CheerioAPI): StockStatus {
|
|||
|
||||
// Check for pre-order / coming soon indicators BEFORE checking add to cart
|
||||
// Some sites show a "Pre-order" button that looks like add to cart
|
||||
// NOTE: Be careful with generic phrases - "available in" matches "available in stock"!
|
||||
const preOrderComingSoonPhrases = [
|
||||
'coming soon',
|
||||
'coming in',
|
||||
'available soon',
|
||||
'available in',
|
||||
'arriving soon',
|
||||
'arriving in',
|
||||
'releases on',
|
||||
'release date',
|
||||
'expected release',
|
||||
|
|
@ -1284,25 +1309,53 @@ function extractGenericStockStatus($: CheerioAPI): StockStatus {
|
|||
'join waitlist',
|
||||
'not yet released',
|
||||
'not yet available',
|
||||
'coming this',
|
||||
'coming next',
|
||||
// Specific future availability phrases (avoid generic "available in" which matches "available in stock")
|
||||
'available starting',
|
||||
'available from', // Usually followed by a date
|
||||
'ships in', // Usually indicates future shipping
|
||||
'expected to ship',
|
||||
'estimated arrival',
|
||||
];
|
||||
|
||||
for (const phrase of preOrderComingSoonPhrases) {
|
||||
if (textToCheck.includes(phrase)) {
|
||||
// Double check it's not just a section about pre-orders in general
|
||||
// by looking for the phrase near price/product context
|
||||
const phraseIndex = textToCheck.indexOf(phrase);
|
||||
const contextStart = Math.max(0, phraseIndex - 200);
|
||||
const contextEnd = Math.min(textToCheck.length, phraseIndex + 200);
|
||||
const context = textToCheck.substring(contextStart, contextEnd);
|
||||
// Phrases that indicate the product is NOT coming soon (should not trigger out of stock)
|
||||
const inStockPhrases = [
|
||||
'in stock',
|
||||
'add to cart',
|
||||
'add to basket',
|
||||
'buy now',
|
||||
'available now',
|
||||
'ships today',
|
||||
'ships immediately',
|
||||
'ready to ship',
|
||||
];
|
||||
|
||||
// If the context mentions price, buy, cart, or product, it's likely about this product
|
||||
if (context.includes('$') || context.includes('price') ||
|
||||
context.includes('buy') || context.includes('cart') ||
|
||||
context.includes('order') || context.includes('purchase')) {
|
||||
return 'out_of_stock';
|
||||
// First, check if the page has strong in-stock indicators
|
||||
// If so, don't let pre-order phrase matching override it
|
||||
let hasInStockIndicator = false;
|
||||
for (const phrase of inStockPhrases) {
|
||||
if (textToCheck.includes(phrase)) {
|
||||
hasInStockIndicator = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only check for pre-order/coming soon if we don't have a clear in-stock indicator
|
||||
if (!hasInStockIndicator) {
|
||||
for (const phrase of preOrderComingSoonPhrases) {
|
||||
if (textToCheck.includes(phrase)) {
|
||||
// Double check it's not just a section about pre-orders in general
|
||||
// by looking for the phrase near price/product context
|
||||
const phraseIndex = textToCheck.indexOf(phrase);
|
||||
const contextStart = Math.max(0, phraseIndex - 200);
|
||||
const contextEnd = Math.min(textToCheck.length, phraseIndex + 200);
|
||||
const context = textToCheck.substring(contextStart, contextEnd);
|
||||
|
||||
// If the context mentions price, buy, cart, or product, it's likely about this product
|
||||
if (context.includes('$') || context.includes('price') ||
|
||||
context.includes('buy') || context.includes('cart') ||
|
||||
context.includes('order') || context.includes('purchase')) {
|
||||
return 'out_of_stock';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue