Merge branch 'origin/antigravity' into antigravity

Resolved conflict in browserless_service.py by merging Amazon interstitial selectors.
Combined local robust selectors with remote additions for maximum coverage.
This commit is contained in:
Michael committed 2025-12-02 09:37:31 +01:00
commit 86aae8f628
6 files changed
+94 -12

No files matched your search

+4
View File
@@ -53,8 +53,12 @@ POPUP_SELECTORS = [
# Amazon Interstitials (Soft blocks)
"button:has-text('Continuer les achats')",
"span:has-text('Continuer les achats')",
"a:has-text('Continuer les achats')",
"input[value='Continuer les achats']",
"input[value='Continue shopping']",
"span.a-button-inner > input.a-button-input[type='submit']",
"form:has-text('Continuer les achats') input[type='submit']",
"[aria-labelledby='continue-shopping-label']",
]
+12
View File
@@ -127,6 +127,18 @@ async def process_item_check(item_id: int):
if not screenshot_path:
raise Exception("Failed to capture screenshot")
# Update the main item screenshot for the UI
try:
import shutil
import os
dest_path = f"/app/screenshots/item_{item_id}.png"
# Ensure directory exists
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
shutil.copy2(screenshot_path, dest_path)
logger.info(f"Updated main screenshot for item {item_id}")
except Exception as e:
logger.error(f"Failed to update main screenshot: {e}")
if not (ai_result := await AIService.analyze_image(screenshot_path, page_text=page_text)):
raise Exception("AI analysis failed")
+4
View File
@@ -422,6 +422,10 @@ def seed_default_sites(db: Session) -> int:
site = existing_sites[clean_domain]
changed = False
for key, value in site_data.items():
# Skip updating user-configurable fields to preserve preferences
if key in ["is_active", "priority"]:
continue
if getattr(site, key) != value:
setattr(site, key, value)
changed = True
@@ -245,17 +245,17 @@ export function ItemCard({ item, onEdit, onDelete, onCheck, onZoom, onCategoryUp
<div className="flex gap-1">
{onViewHistory && (
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => onViewHistory(item)} title="Historique des prix">
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => onViewHistory(item)} title={t('dashboard.history') || "Historique"}>
<History className="h-3 w-3" />
</Button>
)}
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => onEdit(item)}>
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => onEdit(item)} title={t('common.edit') || "Modifier"}>
<Edit2 className="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => onCheck(item.id)} disabled={item.is_refreshing}>
<Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => onCheck(item.id)} disabled={item.is_refreshing} title={t('dashboard.refresh') || "Forcer la mise à jour"}>
<RefreshCw className={cn("h-3 w-3", item.is_refreshing && "animate-spin text-primary")} />
</Button>
<Button variant="ghost" size="icon" className="h-6 w-6 text-destructive hover:text-destructive" onClick={() => onDelete(item)}>
<Button variant="ghost" size="icon" className="h-6 w-6 text-destructive hover:text-destructive" onClick={() => onDelete(item)} title={t('common.delete') || "Supprimer"}>
<Trash2 className="h-3 w-3" />
</Button>
</div>
@@ -138,7 +138,7 @@ export function ItemModal({ item, onClose, onSaved, open, categories = [] }) {
<div className="flex gap-2">
<Input
id="custom_category"
placeholder="Enter new category"
placeholder="Entrer une nouvelle catégorie"
value={formData.category}
onChange={e => setFormData({ ...formData, category: e.target.value })}
autoFocus
@@ -151,7 +151,7 @@ export function ItemModal({ item, onClose, onSaved, open, categories = [] }) {
setShowCustomInput(false);
setFormData({ ...formData, category: '' });
}}
title="Select existing category"
title="Sélectionner une catégorie existante"
>
<X className="h-4 w-4" />
</Button>
@@ -171,30 +171,30 @@ export function ItemModal({ item, onClose, onSaved, open, categories = [] }) {
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a category" />
<SelectValue placeholder="Sélectionner une catégorie" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">None</SelectItem>
<SelectItem value="none">Aucune</SelectItem>
{categories.map(cat => (
<SelectItem key={cat} value={cat}>{cat}</SelectItem>
))}
<SelectItem value="__custom__">Custom...</SelectItem>
<SelectItem value="__custom__">Autre...</SelectItem>
</SelectContent>
</Select>
)}
</div>
<div className="space-y-2">
<Label htmlFor="channel">Notification Channel</Label>
<Label htmlFor="channel">Canal de Notification</Label>
<Select
value={formData.notification_channel_id}
onValueChange={(val) => setFormData({ ...formData, notification_channel_id: val })}
>
<SelectTrigger>
<SelectValue placeholder="Select a channel" />
<SelectValue placeholder="Sélectionner un canal" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">None</SelectItem>
<SelectItem value="none">Aucun</SelectItem>
{channels.map(channel => (
<SelectItem key={channel.id} value={channel.id.toString()}>
{channel.name} ({channel.type})
+62
View File
@@ -0,0 +1,62 @@
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
# Launch with headless=True to mimic server environment
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
)
page = await context.new_page()
# Try a product URL that might trigger the check
url = "https://www.amazon.fr/dp/B07S58MPKW"
print(f"Navigating to {url}")
try:
await page.goto(url)
await page.wait_for_timeout(5000)
content = await page.content()
if "Continuer les achats" in content:
print("🚨 Popup detected!")
with open("amazon_popup.html", "w", encoding="utf-8") as f:
f.write(content)
await page.screenshot(path="amazon_popup.png")
# Analyze the button
print("Searching for button...")
# Try various locators
locators = [
"button",
"input[type='submit']",
"a.a-button-text",
"span.a-button-inner"
]
for sel in locators:
elements = page.locator(sel)
count = await elements.count()
for i in range(count):
el = elements.nth(i)
if await el.is_visible():
txt = await el.inner_text()
val = await el.get_attribute("value") or ""
if "Continuer" in txt or "Continuer" in val:
print(f"✅ Found candidate: {sel}")
print(f" Text: {txt}")
print(f" Value: {val}")
print(f" OuterHTML: {await el.evaluate('el => el.outerHTML')}")
else:
print("No popup detected. Page title:", await page.title())
except Exception as e:
print(f"Error: {e}")
await browser.close()
if __name__ == "__main__":
asyncio.run(main())