Refactor activity image error handling and clean up debug_bestbuy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-01 22:27:06 -04:00
parent d33639fdc7
commit c6bc09c0d3
3 changed files with 320 additions and 871 deletions
+15 -2
View File
@@ -281,7 +281,7 @@ async function loadActivityFeed() {
return ` return `
<div class="activity-item"> <div class="activity-item">
${hasProductImage ${hasProductImage
? `<img src="${event.image_url}" class="activity-thumb" alt="" onerror="this.outerHTML=\`${storeLogoHtml.replace(/`/g, '\\`').replace(/\n/g, '')}\`">` ? `<img src="${event.image_url}" class="activity-thumb" alt="" onerror="handleActivityImageError(this, '${event.site}')">`
: storeLogoHtml : storeLogoHtml
} }
<div class="activity-details"> <div class="activity-details">
@@ -297,6 +297,19 @@ async function loadActivityFeed() {
}).join(''); }).join('');
} }
function handleActivityImageError(img, site) {
const storeInfo = STORE_INFO[site] || { name: site, color: '#333', bgColor: '#666', logo: '', fallback: '?' };
const div = document.createElement('div');
div.className = 'activity-store-icon';
div.style.background = storeInfo.bgColor;
div.innerHTML = `
<img src="${storeInfo.logo}" alt="${storeInfo.name}" class="activity-store-logo"
onerror="this.style.display='none'; this.nextElementSibling.style.display='block';">
<span class="activity-store-fallback" style="display:none; color:${storeInfo.color};">${storeInfo.fallback}</span>
`;
img.replaceWith(div);
}
// Navigate to products filtered by event type // Navigate to products filtered by event type
function goToFilteredProducts(eventType, period) { function goToFilteredProducts(eventType, period) {
currentEventFilter = { type: eventType, period: period }; currentEventFilter = { type: eventType, period: period };
@@ -718,7 +731,7 @@ function cleanProductName(name) {
.replace(/\{[^}]*\}/g, '') // Remove any remaining {css} blocks .replace(/\{[^}]*\}/g, '') // Remove any remaining {css} blocks
.replace(/\d+\s*Reviews?$/i, '') // Remove trailing "X Reviews" .replace(/\d+\s*Reviews?$/i, '') // Remove trailing "X Reviews"
// Fix Pokémon - multiple encoding patterns // Fix Pokémon - multiple encoding patterns
.replace(/Pok[éÃ\u00c3\u00a9]+mon/gi, 'Pokémon') .replace(/Pok[éÃ\u00c3\u00a9\ufffd]+mon/gi, 'Pokémon')
.replace(/Pokémon/gi, 'Pokémon') .replace(/Pokémon/gi, 'Pokémon')
.replace(/Pok&eacute;mon/gi, 'Pokémon') .replace(/Pok&eacute;mon/gi, 'Pokémon')
.replace(/Pokテゥmon/gi, 'Pokémon') .replace(/Pokテゥmon/gi, 'Pokémon')
+280 -851
View File
File diff suppressed because one or more lines are too long
+8 -1
View File
@@ -5,6 +5,7 @@ Now integrates with SQLite database for historical tracking.
import json import json
import logging import logging
import re
from pathlib import Path from pathlib import Path
from typing import Dict, List, Set, Optional, Tuple from typing import Dict, List, Set, Optional, Tuple
from dataclasses import asdict from dataclasses import asdict
@@ -53,6 +54,12 @@ class ProductTracker:
except IOError as e: except IOError as e:
logger.error(f"Error saving products file: {e}") logger.error(f"Error saving products file: {e}")
def _normalize_url(self, url: str) -> str:
"""Normalize product URL to avoid tracking the same product under different URLs"""
# Best Buy: strip /sku/XXXXX variant suffix — same product, different SKU variants
url = re.sub(r'/sku/\d+$', '', url)
return url.rstrip('/')
def process_products(self, products: List[Product]) -> Tuple[List[Product], List[Product]]: def process_products(self, products: List[Product]) -> Tuple[List[Product], List[Product]]:
""" """
Process a list of scraped products and detect changes. Process a list of scraped products and detect changes.
@@ -68,7 +75,7 @@ class ProductTracker:
restocked_products = [] restocked_products = []
for product in products: for product in products:
url = product.url url = self._normalize_url(product.url)
now = datetime.now().isoformat() now = datetime.now().isoformat()
# Get or create product in database # Get or create product in database