[object Object]

← back to Wallco Ai

Add pilot-build-tifs.py (PG-drift-proof TIF builder via by-id route)

d668131a28b0d2c6d0a578423cb36bc1cd1b22eb · 2026-06-02 08:23:54 -0700 · Steve Abrams

Builds data/tif/<id>.tif by pulling each source from the server's own
/designs/img/by-id/:id route (resolves correctly on prod regardless of the
sparse all_designs / designs.json-has-no-tif_path drift). Shipped via deploy.
For the 20-design prod pilot of the full-res TIF features. Verified locally:
build -> hires route serves 200 image/jpeg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit d668131a28b0d2c6d0a578423cb36bc1cd1b22eb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jun 2 08:23:54 2026 -0700

    Add pilot-build-tifs.py (PG-drift-proof TIF builder via by-id route)
    
    Builds data/tif/<id>.tif by pulling each source from the server's own
    /designs/img/by-id/:id route (resolves correctly on prod regardless of the
    sparse all_designs / designs.json-has-no-tif_path drift). Shipped via deploy.
    For the 20-design prod pilot of the full-res TIF features. Verified locally:
    build -> hires route serves 200 image/jpeg.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 deploy-kamatera.sh          |  2 +-
 scripts/pilot-build-tifs.py | 74 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/deploy-kamatera.sh b/deploy-kamatera.sh
index 0095d99..b1c4916 100755
--- a/deploy-kamatera.sh
+++ b/deploy-kamatera.sh
@@ -106,7 +106,7 @@ rsync -az "$LOCAL_DIR/scripts/refresh_designs_snapshot.py" \
 #     for those features to work. List grows narrowly as endpoints add deps;
 #     don't unbox scripts/ wholesale.
 echo "[2d/6] ship runtime-endpoint scripts..."
-for script in scripts/seam-defect-boxes.py scripts/mint-shopify-download-token.js scripts/generate_designs.js scripts/generator_tick.js scripts/seam-heal-feather.py scripts/quantize-no-ghost.py scripts/recolor-tif.py scripts/tif-to-web.py; do
+for script in scripts/seam-defect-boxes.py scripts/mint-shopify-download-token.js scripts/generate_designs.js scripts/generator_tick.js scripts/seam-heal-feather.py scripts/quantize-no-ghost.py scripts/recolor-tif.py scripts/tif-to-web.py scripts/pilot-build-tifs.py; do
   if [ -f "$LOCAL_DIR/$script" ]; then
     rsync -az "$LOCAL_DIR/$script" "$REMOTE:$REMOTE_DIR/$script" && echo "  $script shipped"
   fi
diff --git a/scripts/pilot-build-tifs.py b/scripts/pilot-build-tifs.py
new file mode 100644
index 0000000..ccd39a9
--- /dev/null
+++ b/scripts/pilot-build-tifs.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+"""pilot-build-tifs.py — build full-size design TIFs for a set of ids, sourcing
+the image from the running server's own /designs/img/by-id/:id route.
+
+Why not scripts/build-tif.py for the pilot: that one reads metadata + local_path
+from `all_designs`, which is the SPARSE ~350-row table on prod (the catalog
+lives in data/designs.json / spoon_all_designs) — so it returns "not found" for
+most published designs, and its source resolution depends on local_path being
+present + correct. The by-id route already resolves the on-disk source PNG
+correctly on prod (PG-drift-proof), so we pull from it over localhost and write
+data/tif/<id>.tif. /designs/hires/:id (findTifOnDisk) then serves it, and the
+colorway recolor can use it as the full-res source.
+
+NOTE: the TIF is exactly as sharp as the source PNG the server serves (≈1024px
+for current generations) — this does NOT synthesize detail. It makes the TIF
+asset + the single-image mural preview real, and lets the edge-preserving
+recolor run server-side. A genuinely higher-res master needs a higher-res
+generation/upscale pipeline (separate decision).
+
+CLI:  pilot-build-tifs.py [--base http://127.0.0.1:9905] [--force] <id> [<id> ...]
+Prints one line per id. Exit 0 if all ok, 1 if any failed.
+"""
+import sys, os, io, argparse, urllib.request
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[1]
+TIF_DIR = ROOT / "data" / "tif"
+
+
+def build(base, design_id, force):
+    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}"
+    try:
+        from PIL import Image
+        Image.MAX_IMAGE_PIXELS = None
+        im = Image.open(io.BytesIO(data)).convert("RGB")
+        w, h = im.size
+        TIF_DIR.mkdir(parents=True, exist_ok=True)
+        im.save(out, format="TIFF", compression="tiff_lzw", dpi=(150, 150))
+        return True, f"{design_id}: TIF {w}x{h} -> {out} ({out.stat().st_size:,} B)"
+    except Exception as e:
+        return False, f"{design_id}: build fail: {e}"
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    ap.add_argument("--base", default="http://127.0.0.1:9905")
+    ap.add_argument("--force", action="store_true")
+    ap.add_argument("ids", nargs="+")
+    a = ap.parse_args()
+    ok_all = True
+    for raw in a.ids:
+        try:
+            did = int(raw)
+        except ValueError:
+            print(f"{raw}: not an int"); ok_all = False; continue
+        ok, msg = build(a.base, did, a.force)
+        print(("  ok  " if ok else "  FAIL ") + msg)
+        ok_all = ok_all and ok
+    return 0 if ok_all else 1
+
+
+if __name__ == "__main__":
+    sys.exit(main())

← c9f7813 TIF lookup: match both <id>.tif and <category>/design_<id>.t  ·  back to Wallco Ai  ·  edges-review + seam-debug + PDP: status filter + per-surface 2b09de8 →