SPB Git forge

spb/immo-ka

Public

Immo-Ka — agrégateur des propriétés à vendre au Québec (73 connecteurs, ~40 000 annonces, React+FastAPI)

112commits 1branches 0releases
125.4 MBsize
maindefault branch
13 days agolast push
Python 47.5% HTML 27.9% TypeScript 15.5% CSS 7.2% JavaScript 2%

imgaudit : verdict « morte » sur preuve seulement (404/410) — les 403/429 des CDN (rate-limit DuProprio, anti-hotlink) ne retirent plus d images ; commits par tranches (plus de verrou BD long) ; 8 workers

Simon-Pierre Boucher committed 1 mo ago (Aug 19, 2026) parent bc26ae5

1 changed file +12 −4

modified immoka/imgaudit.py +12 −4
@@ -124,13 +124,20 @@ def image_size(data: bytes) -> tuple[int, int] | None:
124 124
125 125
126 126 def check_url(url: str) -> dict:
127 − """Vérifie une URL d'image : {ok, status, width, height, bytes, reason}."""
127 + """Vérifie une URL d'image : {ok, status, width, height, bytes, reason}.
128 +
129 + ⚠ Verdict « morte » UNIQUEMENT sur preuve solide (404/410) : un 403/429 est
130 + presque toujours du rate-limiting ou de l'anti-hotlink du CDN (bug payé :
131 + ~6 000 galeries DuProprio retirées à tort). En cas de doute on garde
132 + l'image — le repli onError du frontend couvre les rares vraies mortes."""
128 133 try:
129 134 r = requests.get(url, headers={"User-Agent": UA, "Range": "bytes=0-65535"},
130 135 timeout=TIMEOUT, stream=True)
131 136 status = r.status_code
132 − if status >= 400:
137 + if status in (404, 410):
133 138 return {"ok": 0, "status": status, "reason": "http"}
139 + if status >= 400:
140 + return {"ok": 1, "status": status, "reason": "non_verifiable"}
134 141 data = next(r.iter_content(65536), b"") or b""
135 142 r.close()
136 143 total = int((r.headers.get("Content-Range") or "/0").split("/")[-1] or 0) \
@@ -151,7 +158,8 @@ def check_url(url: str) -> dict:
151 158 "bytes": total, "reason": "poids_suspect"}
152 159 return {"ok": 1, "status": status, "width": w, "height": h, "bytes": total}
153 160 except requests.RequestException:
154 − return {"ok": 0, "status": 0, "reason": "timeout"}
161 + # réseau/timeout : non concluant — ne jamais retirer sur un doute
162 + return {"ok": 1, "status": 0, "reason": "non_verifiable"}
155 163
156 164
157 165 # ---------------------------------------------------------------------------
@@ -221,7 +229,7 @@ def audit_listing(con: sqlite3.Connection, uid: str, images: list[str],
221 229 return new_images, details, checked
222 230
223 231
224 −def run_batch(limit: int = 2000, workers: int = 24) -> dict:
232 +def run_batch(limit: int = 2000, workers: int = 8) -> dict:
225 233 """Audit réseau budgété : les annonces publiées jamais auditées d'abord.
226 234
227 235 Appelé après chaque synchronisation (ingest.watch) ; relancer avec un gros
228 236