← back to Wallco Ai
pilot-build-tifs.py: source from design image_url, fall back to by-id
4b22f53c7930aebc5ba2d791e3e1dead9f13b1c4 · 2026-06-02 09:19:22 -0700 · Steve Abrams
The 408 'unbuildable' designs (mostly designer-scenic) were a false negative —
they have images served via a direct /designs/img/<file>.png that the by-id
route can't resolve (no local_path), so by-id 404'd. Now build() tries the
design's own image_url first (passed from designs.json in --all), then by-id.
First 200 wins. Turns the 408 into buildable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M scripts/pilot-build-tifs.py
Diff
commit 4b22f53c7930aebc5ba2d791e3e1dead9f13b1c4
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 2 09:19:22 2026 -0700
pilot-build-tifs.py: source from design image_url, fall back to by-id
The 408 'unbuildable' designs (mostly designer-scenic) were a false negative —
they have images served via a direct /designs/img/<file>.png that the by-id
route can't resolve (no local_path), so by-id 404'd. Now build() tries the
design's own image_url first (passed from designs.json in --all), then by-id.
First 200 wins. Turns the 408 into buildable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/pilot-build-tifs.py | 64 ++++++++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 21 deletions(-)
diff --git a/scripts/pilot-build-tifs.py b/scripts/pilot-build-tifs.py
index 2c9d788..8752a1f 100644
--- a/scripts/pilot-build-tifs.py
+++ b/scripts/pilot-build-tifs.py
@@ -27,19 +27,35 @@ ROOT = Path(__file__).resolve().parents[1]
TIF_DIR = ROOT / "data" / "tif"
-def build(base, design_id, force):
+def _fetch(base, path_or_url):
+ url = path_or_url if path_or_url.startswith("http") else (base + path_or_url)
+ req = urllib.request.Request(url, headers={"User-Agent": "pilot-build-tifs"})
+ with urllib.request.urlopen(req, timeout=30) as r:
+ if r.status != 200:
+ raise RuntimeError(f"HTTP {r.status}")
+ return r.read()
+
+
+def build(base, design_id, force, image_url=None):
out = TIF_DIR / f"{design_id}.tif"
if out.exists() and not force:
return True, f"{design_id}: exists ({out.stat().st_size:,} B) — --force to rebuild"
- url = f"{base}/designs/img/by-id/{design_id}"
- try:
- req = urllib.request.Request(url, headers={"User-Agent": "pilot-build-tifs"})
- with urllib.request.urlopen(req, timeout=30) as r:
- if r.status != 200:
- return False, f"{design_id}: source HTTP {r.status}"
- data = r.read()
- except Exception as e:
- return False, f"{design_id}: fetch fail: {e}"
+ # Source candidates, in order: the design's own image_url (older designs use
+ # a direct /designs/img/<file>.png that the by-id route can't resolve — no
+ # local_path), then the by-id route. First 200 wins.
+ candidates = []
+ if image_url and isinstance(image_url, str) and ("/designs/img/" in image_url or image_url.startswith("http")):
+ candidates.append(image_url)
+ candidates.append(f"/designs/img/by-id/{design_id}")
+ data, errs = None, []
+ for c in candidates:
+ try:
+ data = _fetch(base, c)
+ break
+ except Exception as e:
+ errs.append(f"{c.split('/')[-1]}:{e}")
+ if data is None:
+ return False, f"{design_id}: fetch fail: {' | '.join(errs)}"
try:
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
@@ -73,43 +89,49 @@ def main():
ap.add_argument("ids", nargs="*")
a = ap.parse_args()
+ # items = list of (id, image_url|None). image_url lets build() source the
+ # design's actual PNG when the by-id route can't resolve it (older designs).
if a.all or a.all_including_unpublished:
import json
p = ROOT / "data" / "designs.json"
arr = json.loads(p.read_text())
if isinstance(arr, dict):
arr = arr.get("designs", [])
- ids = []
+ seen, items = set(), []
for d in arr:
if a.all and d.get("is_published") is False:
continue
try:
- ids.append(int(d["id"]))
+ did = int(d["id"])
except (KeyError, ValueError, TypeError):
- pass
- ids = sorted(set(ids))
+ continue
+ if did in seen:
+ continue
+ seen.add(did)
+ items.append((did, d.get("image_url")))
+ items.sort(key=lambda t: t[0])
else:
- ids = []
+ items = []
for raw in a.ids:
try:
- ids.append(int(raw))
+ items.append((int(raw), None))
except ValueError:
print(f"{raw}: not an int")
if a.start:
- ids = ids[a.start:]
+ items = items[a.start:]
if a.limit:
- ids = ids[:a.limit]
+ items = items[:a.limit]
- total = len(ids)
+ total = len(items)
print(f"[pilot-build-tifs] {total} ids to consider · free {free_gb():.1f} GB · base {a.base}", flush=True)
ok_all = True
built = skipped = failed = 0
- for i, did in enumerate(ids, 1):
+ for i, (did, image_url) in enumerate(items, 1):
if free_gb() < a.min_free_gb:
print(f"ABORT: free disk {free_gb():.1f} GB < --min-free-gb {a.min_free_gb}. "
f"Built {built}, stopped at id {did} (#{i}/{total}).", flush=True)
return 2
- ok, msg = build(a.base, did, a.force)
+ ok, msg = build(a.base, did, a.force, image_url)
if ok:
built += 1
if "exists" in msg:
← fe12244 PDP: universal theme customizer — render-a-room + free drag-
·
back to Wallco Ai
·
PDP theme switcher: fix dropdown on bare /design/:id — treat 1044ed0 →