← back to Corkwallcovering
Re-host 23 logo-cropped cork images as live Shopify product images; update products.json to new CDN URLs
8d520278ad7b66f4d2760715514d2186b7e8e638 · 2026-06-30 16:34:47 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M data/products.jsonA data/rehost-cropped-results.jsonA scripts/rehost-cropped-cork.js
Diff
commit 8d520278ad7b66f4d2760715514d2186b7e8e638
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 30 16:34:47 2026 -0700
Re-host 23 logo-cropped cork images as live Shopify product images; update products.json to new CDN URLs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
data/products.json | 48 ++++++------
data/rehost-cropped-results.json | 163 ++++++++++++++++++++++++++++++++++++++
scripts/rehost-cropped-cork.js | 165 +++++++++++++++++++++++++++++++++++++++
3 files changed, 352 insertions(+), 24 deletions(-)
diff --git a/data/products.json b/data/products.json
index 4d8fae9..75eeded 100644
--- a/data/products.json
+++ b/data/products.json
@@ -2039,7 +2039,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/43a2aa9b40b4e3e80fad9f478a92f125_c9e2a136-03a2-4d8d-bb78-72f1675aac0e.jpg?v=1745457023",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34178.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -4659,7 +4659,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/e018f8a3dfd210857fe67c8b2b7ba357_0058d1ca-0dcb-4949-ba4e-c4a53aa8816c.jpg?v=1745457027",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34176.jpg",
"tags": [
"Architectural",
"Beige",
@@ -4692,7 +4692,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/5f56df919efeaf3368db50a01fc05cb2_5b979270-553c-492a-9ff6-9162e861ebef.jpg?v=1745457013",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34183.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -4724,7 +4724,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/3ba26b0a8af19094f37c2db0694fc60c_06c69f17-bd41-4986-9096-2726b266727d.jpg?v=1745457009",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34185.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -4757,7 +4757,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/e0e4f3337edc54c774e4347f527ecd75_54967173-52a3-4d8c-b823-32907798a9da.jpg?v=1745457017",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34181.jpg",
"tags": [
"Architectural",
"Beige",
@@ -4788,7 +4788,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/2113990fe114e980f1d501fb7f069f4e_93f78c72-f9c5-4e78-a698-6af0284dd4bd.jpg?v=1745457019",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34180.jpg",
"tags": [
"Architectural",
"Beige",
@@ -4819,7 +4819,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/622d2e0a9a2032b11d3d8c9e012afd04_d66944e1-c338-4382-88e8-66a79917757c.jpg?v=1745457021",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34179.jpg",
"tags": [
"Architectural",
"Beige",
@@ -4851,7 +4851,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/4a10302ba32ad9bebb385088c0b1c8d5_6639e138-54b9-47e9-b6d1-ba61a9541b9e.jpg?v=1745457025",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34177.jpg",
"tags": [
"Architectural",
"Brown",
@@ -4882,7 +4882,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/f88d206c12f2405f59d494ba116994a7_3233a3d0-5f59-4096-9417-24fcbfd42586.jpg?v=1745457011",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34184.jpg",
"tags": [
"Architectural",
"Brown",
@@ -4913,7 +4913,7 @@
"title": "Faguli Real Cork | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/b4f9a63f50e48cc51b575fc15b1f9305_133f25f8-54d8-428b-964f-45a5c880b93d.jpg?v=1745457015",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34182.jpg",
"tags": [
"Architectural",
"Beige",
@@ -5679,7 +5679,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/f17920060be22c14011d9b8693933060.jpg?v=1745456184",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74040.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -5712,7 +5712,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/66c1f555d63f11648f31137f661dbe27.jpg?v=1745456181",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74041.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -5776,7 +5776,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/367de05b47ed67a63e79363cadf9f203.jpg?v=1745456166",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74048.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -7956,7 +7956,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/0d57e40ef1d2e8127a4c2f4723fe621d.jpg?v=1745456186",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74039.jpg",
"tags": [
"Abstract",
"Architectural",
@@ -7988,7 +7988,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/676a51eb91ea689a8faf7da1246860ef.jpg?v=1745456161",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74050.jpg",
"tags": [
"Abstract",
"AI-Analyzed-v2",
@@ -8086,7 +8086,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/7038db2ed0703f48b8e56ce0142d2ac8.jpg?v=1745456191",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74037.jpg",
"tags": [
"Architectural",
"Brown",
@@ -8148,7 +8148,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/60f2f021774d2b25bfb4b238d7493131.jpg?v=1745456150",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74055.jpg",
"tags": [
"Abstract",
"AI-Analyzed-v2",
@@ -8218,7 +8218,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/058a2415387a12f5153beb947a24e0d9.jpg?v=1745456157",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74052.jpg",
"tags": [
"Abstract",
"AI-Analyzed-v2",
@@ -8289,7 +8289,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/2e0c1bae163e2a484be0c9a47449f403.jpg?v=1745456141",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74059.jpg",
"tags": [
"Abstract",
"AI-Analyzed-v2",
@@ -8320,7 +8320,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/c305292ba73b06e8617f42b37487dd4f.jpg?v=1745456154",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74053.jpg",
"tags": [
"Abstract",
"AI-Analyzed-v2",
@@ -8356,7 +8356,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/0a7d319a25a554a9ac146d27d82d5f2a.jpg?v=1745456159",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74051.jpg",
"tags": [
"Abstract",
"AI-Analyzed-v2",
@@ -8428,7 +8428,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/bc112f88c065de11d24240a96af1b638.jpg?v=1745456168",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74047.jpg",
"tags": [
"Architectural",
"Brown",
@@ -8550,7 +8550,7 @@
"title": "La Corka | Phillipe Romano",
"vendor": "Phillipe Romano",
"product_type": "Wallcovering",
- "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/2097cd68e3568c7c8ba10a780f05dfe4.jpg?v=1745456179",
+ "image_url": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74042.jpg",
"tags": [
"Architectural",
"Brown",
@@ -14445,4 +14445,4 @@
"created_at": "2022-02-28T17:45:19-08:00",
"dominant_hex_real": "#aaabac"
}
-]
\ No newline at end of file
+]
diff --git a/data/rehost-cropped-results.json b/data/rehost-cropped-results.json
new file mode 100644
index 0000000..c137da4
--- /dev/null
+++ b/data/rehost-cropped-results.json
@@ -0,0 +1,163 @@
+[
+ {
+ "sku": "faguli-real-cork-cork-34178",
+ "handle": "faguli-real-cork-cork-34178",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34178.jpg?v=1782862328"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34176",
+ "handle": "faguli-real-cork-cork-34176",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34176.jpg?v=1782862333"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34183",
+ "handle": "faguli-real-cork-cork-34183",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34183.jpg?v=1782862338"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34185",
+ "handle": "faguli-real-cork-cork-34185",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34185.jpg?v=1782862343"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34181",
+ "handle": "faguli-real-cork-cork-34181",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34181.jpg?v=1782862348"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34180",
+ "handle": "faguli-real-cork-cork-34180",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34180.jpg?v=1782862353"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34179",
+ "handle": "faguli-real-cork-cork-34179",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34179.jpg?v=1782862358"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34177",
+ "handle": "faguli-real-cork-cork-34177",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34177.jpg?v=1782862364"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34184",
+ "handle": "faguli-real-cork-cork-34184",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34184.jpg?v=1782862369"
+ },
+ {
+ "sku": "faguli-real-cork-cork-34182",
+ "handle": "faguli-real-cork-cork-34182",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/faguli-real-cork-cork-34182.jpg?v=1782862374"
+ },
+ {
+ "sku": "la-corka-prt-74040",
+ "handle": "la-corka-prt-74040",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 10 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74040.jpg?v=1782862379"
+ },
+ {
+ "sku": "la-corka-prt-74041",
+ "handle": "la-corka-prt-74041",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 12 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74041.jpg?v=1782862384"
+ },
+ {
+ "sku": "la-corka-prt-74048",
+ "handle": "la-corka-prt-74048",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74048.jpg?v=1782862390"
+ },
+ {
+ "sku": "la-corka-prt-74039",
+ "handle": "la-corka-prt-74039",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74039.jpg?v=1782862395"
+ },
+ {
+ "sku": "la-corka-prt-74050",
+ "handle": "la-corka-prt-74050",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74050.jpg?v=1782862400"
+ },
+ {
+ "sku": "la-corka-prt-74037",
+ "handle": "la-corka-prt-74037",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74037.jpg?v=1782862405"
+ },
+ {
+ "sku": "la-corka-prt-74055",
+ "handle": "la-corka-prt-74055",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74055.jpg?v=1782862411"
+ },
+ {
+ "sku": "la-corka-prt-74052",
+ "handle": "la-corka-prt-74052",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74052.jpg?v=1782862416"
+ },
+ {
+ "sku": "la-corka-prt-74059",
+ "handle": "la-corka-prt-74059",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74059.jpg?v=1782862421"
+ },
+ {
+ "sku": "la-corka-prt-74053",
+ "handle": "la-corka-prt-74053",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74053.jpg?v=1782862426"
+ },
+ {
+ "sku": "la-corka-prt-74051",
+ "handle": "la-corka-prt-74051",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74051.jpg?v=1782862431"
+ },
+ {
+ "sku": "la-corka-prt-74047",
+ "handle": "la-corka-prt-74047",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 1 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74047.jpg?v=1782862437"
+ },
+ {
+ "sku": "la-corka-prt-74042",
+ "handle": "la-corka-prt-74042",
+ "status": "OK",
+ "detail": "verified: cropped at position 1, banner gone (now 9 imgs)",
+ "newSrc": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/la-corka-prt-74042.jpg?v=1782862442"
+ }
+]
\ No newline at end of file
diff --git a/scripts/rehost-cropped-cork.js b/scripts/rehost-cropped-cork.js
new file mode 100644
index 0000000..43dd7b7
--- /dev/null
+++ b/scripts/rehost-cropped-cork.js
@@ -0,0 +1,165 @@
+#!/usr/bin/env node
+/* Re-host the 23 logo-header-cropped cork images as the live product images.
+ * Idempotent, serial, one product at a time, verify each.
+ * Steve APPROVED ("all go") 2026-06-30.
+ */
+const fs = require('fs');
+const path = require('path');
+
+const ROOT = path.resolve(__dirname, '..');
+const SECRETS = '/Users/stevestudio2/Projects/secrets-manager/.env';
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+
+// --- load admin token ---
+const envTxt = fs.readFileSync(SECRETS, 'utf8');
+const m = envTxt.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m);
+if (!m) { console.error('No SHOPIFY_ADMIN_TOKEN in secrets'); process.exit(1); }
+const TOKEN = m[1].trim();
+
+const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', 'cropped-manifest.json'), 'utf8'));
+const productsPath = path.join(ROOT, 'data', 'products.json');
+const products = JSON.parse(fs.readFileSync(productsPath, 'utf8'));
+
+const base = `https://${SHOP}/admin/api/${API}`;
+const headers = { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' };
+
+const sleep = (ms) => new Promise(r => setTimeout(r, ms));
+
+// normalize a Shopify CDN url to its path (strip query string) for comparison
+function normUrl(u) {
+ if (!u) return '';
+ try { const x = new URL(u); return x.origin + x.pathname; }
+ catch { return u.split('?')[0]; }
+}
+
+async function shopify(method, urlPath, body) {
+ const res = await fetch(base + urlPath, {
+ method, headers, body: body ? JSON.stringify(body) : undefined,
+ });
+ const text = await res.text();
+ let json = null;
+ try { json = text ? JSON.parse(text) : null; } catch { /* keep text */ }
+ return { ok: res.ok, status: res.status, json, text, headers: res.headers };
+}
+
+async function getProductByHandle(handle) {
+ // handle is unique; query products by handle
+ const r = await shopify('GET', `/products.json?handle=${encodeURIComponent(handle)}&fields=id,handle,title,images`);
+ if (!r.ok) return { error: `lookup HTTP ${r.status}: ${r.text.slice(0,200)}` };
+ const list = (r.json && r.json.products) || [];
+ const exact = list.find(p => p.handle === handle);
+ return { product: exact || null, raw: list };
+}
+
+async function getFullProduct(id) {
+ const r = await shopify('GET', `/products/${id}.json?fields=id,handle,images`);
+ if (!r.ok) return { error: `get HTTP ${r.status}` };
+ return { product: r.json.product };
+}
+
+const results = [];
+
+(async () => {
+ console.log(`Processing ${manifest.length} cork SKUs...\n`);
+ for (const item of manifest) {
+ const { sku, handle, cropped_file, image_url: bannerUrl } = item;
+ const log = (msg) => console.log(`[${sku}] ${msg}`);
+ const rec = { sku, handle, status: 'PENDING', detail: '', newSrc: '' };
+ try {
+ // 1. resolve product
+ const lookup = await getProductByHandle(handle);
+ if (lookup.error) { rec.status = 'SKIP'; rec.detail = `lookup failed: ${lookup.error}`; log(rec.detail); results.push(rec); await sleep(700); continue; }
+ const product = lookup.product;
+ if (!product) { rec.status = 'SKIP'; rec.detail = 'handle did not resolve to a product'; log(rec.detail); results.push(rec); await sleep(700); continue; }
+ const pid = product.id;
+ log(`product_id=${pid} ("${product.title}") images=${product.images.length}`);
+
+ // 2. identify banner image (matches manifest original image_url by path)
+ const bannerNorm = normUrl(bannerUrl);
+ let banner = product.images.find(img => normUrl(img.src) === bannerNorm);
+
+ // idempotency: detect if a cropped image already added (filename basename of cropped_file present in some src)
+ const croppedBase = path.basename(cropped_file).replace(/\.[a-z]+$/i, '');
+ const alreadyCropped = product.images.find(img => (img.src || '').includes(croppedBase));
+
+ if (!banner && alreadyCropped) {
+ rec.status = 'OK';
+ rec.detail = 'idempotent: banner already gone, cropped image already present';
+ rec.newSrc = alreadyCropped.src;
+ log(rec.detail + ' -> ' + alreadyCropped.src);
+ // ensure products.json carries the cropped src
+ updateProductsJson(sku, handle, alreadyCropped.src.split('?')[0]);
+ results.push(rec); await sleep(700); continue;
+ }
+ if (!banner) { rec.status = 'SKIP'; rec.detail = `no image matching banner url (${bannerNorm}); not guessing`; log(rec.detail); results.push(rec); await sleep(700); continue; }
+ log(`banner image id=${banner.id} pos=${banner.position}`);
+
+ // 3. upload cropped as new image at position 1
+ const filePath = path.join(ROOT, 'public', cropped_file);
+ if (!fs.existsSync(filePath)) { rec.status = 'SKIP'; rec.detail = `cropped file missing: ${filePath}`; log(rec.detail); results.push(rec); await sleep(700); continue; }
+ const b64 = fs.readFileSync(filePath).toString('base64');
+ const filename = path.basename(cropped_file);
+ const up = await shopify('POST', `/products/${pid}/images.json`, {
+ image: { attachment: b64, filename, position: 1 }
+ });
+ if (!up.ok) { rec.status = 'SKIP'; rec.detail = `upload failed HTTP ${up.status}: ${up.text.slice(0,200)}`; log(rec.detail); results.push(rec); await sleep(1000); continue; }
+ const newImg = up.json.image;
+ log(`uploaded new image id=${newImg.id} src=${newImg.src}`);
+ rec.newSrc = newImg.src;
+ await sleep(800);
+
+ // 4. delete the old banner image
+ const del = await shopify('DELETE', `/products/${pid}/images/${banner.id}.json`);
+ if (!del.ok) { rec.status = 'PARTIAL'; rec.detail = `uploaded cropped (id ${newImg.id}) but DELETE of banner ${banner.id} failed HTTP ${del.status}`; log(rec.detail); results.push(rec); await sleep(1000); continue; }
+ log(`deleted banner image id=${banner.id}`);
+ await sleep(800);
+
+ // 5. verify
+ const verify = await getFullProduct(pid);
+ if (verify.error) { rec.status = 'PARTIAL'; rec.detail = `actions done but verify GET failed: ${verify.error}`; results.push(rec); await sleep(800); continue; }
+ const imgs = verify.product.images;
+ const bannerGone = !imgs.find(i => i.id === banner.id);
+ const newPresent = imgs.find(i => i.id === newImg.id);
+ const pos1 = imgs.find(i => i.position === 1);
+ const pos1IsCropped = pos1 && newPresent && pos1.id === newImg.id;
+ if (bannerGone && newPresent && pos1IsCropped) {
+ rec.status = 'OK';
+ rec.detail = `verified: cropped at position 1, banner gone (now ${imgs.length} imgs)`;
+ rec.newSrc = newPresent.src;
+ log(rec.detail);
+ } else {
+ rec.status = 'PARTIAL';
+ rec.detail = `verify mismatch: bannerGone=${bannerGone} newPresent=${!!newPresent} pos1IsCropped=${pos1IsCropped}`;
+ if (newPresent) rec.newSrc = newPresent.src;
+ log(rec.detail);
+ }
+
+ // 6. update products.json with new cropped CDN url (strip ?v for stability)
+ if (rec.newSrc) updateProductsJson(sku, handle, rec.newSrc.split('?')[0]);
+
+ } catch (e) {
+ rec.status = 'SKIP'; rec.detail = 'exception: ' + (e && e.message);
+ log(rec.detail);
+ }
+ results.push(rec);
+ await sleep(900); // rate-limit gap between SKUs
+ }
+
+ // persist products.json once at end (updates applied in-memory)
+ fs.writeFileSync(productsPath, JSON.stringify(products, null, 2) + '\n');
+
+ // summary
+ console.log('\n===== SUMMARY =====');
+ const ok = results.filter(r => r.status === 'OK').length;
+ const skip = results.filter(r => r.status === 'SKIP').length;
+ const partial = results.filter(r => r.status === 'PARTIAL').length;
+ for (const r of results) console.log(`${r.status.padEnd(8)} ${r.sku} ${r.detail}`);
+ console.log(`\nOK=${ok} PARTIAL=${partial} SKIP=${skip} total=${results.length}`);
+ fs.writeFileSync(path.join(ROOT, 'data', 'rehost-cropped-results.json'), JSON.stringify(results, null, 2));
+})();
+
+function updateProductsJson(sku, handle, newUrl) {
+ const p = products.find(x => x && (x.sku === sku || x.handle === handle));
+ if (p) { p.image_url = newUrl; }
+}
← a6f5277 Crop PHILLIPE ROMANO logo-header band off 23 cork product im
·
back to Corkwallcovering
·
auto-save: 2026-07-01T09:41:09 (1 files) — scripts/enrich-co 6bbcce9 →