← back to Dw Fleet Registry
rooms: niche-matched pattern picker + regen (fixes off-niche/sample-crop heroes)
feeb0596859d47b0677c0040a9271fbe957d9386 · 2026-06-01 16:24:47 -0700 · SteveStudio2
Picks each site's wall pattern by domain-niche keyword + non-sample preference instead
of the newest product. Regenerates only sites where the pattern actually changes (cost-
minimal). Re-ran 17/45: era sites get period patterns (1950s vintage utensils, 1960s
geometric), chinoiserie gets real bamboo trellis vs a sample crop, jute gets actual jute.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A regen-better-patterns.mjs
Diff
commit feeb0596859d47b0677c0040a9271fbe957d9386
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Mon Jun 1 16:24:47 2026 -0700
rooms: niche-matched pattern picker + regen (fixes off-niche/sample-crop heroes)
Picks each site's wall pattern by domain-niche keyword + non-sample preference instead
of the newest product. Regenerates only sites where the pattern actually changes (cost-
minimal). Re-ran 17/45: era sites get period patterns (1950s vintage utensils, 1960s
geometric), chinoiserie gets real bamboo trellis vs a sample crop, jute gets actual jute.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
regen-better-patterns.mjs | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/regen-better-patterns.mjs b/regen-better-patterns.mjs
new file mode 100644
index 0000000..2c3c516
--- /dev/null
+++ b/regen-better-patterns.mjs
@@ -0,0 +1,37 @@
+import fs from 'node:fs';
+import { execFileSync } from 'node:child_process';
+const niche = d => d.replace(/\.com$/,'').replace(/(wallpapers?|wallcoverings?|walls?|wallpaper)$/,'').replace(/s$/,'');
+function better(items, key){
+ const match = items.filter(p => (((p.title||'')+' '+(Array.isArray(p.tags)?p.tags.join(' '):'')).toLowerCase()).includes(key));
+ return (match.find(p=>!/sample|swatch|cover|digital|download/i.test(p.image_url)) || match[0] || items.find(p=>!/sample|swatch|cover|digital|download/i.test(p.image_url)) || items[0]);
+}
+const ROOMS=[{roomType:'living_room',angle:'straight_on',cameraDistance:8},{roomType:'office',angle:'angled_left',cameraDistance:11},{roomType:'dining_room',angle:'straight_on',cameraDistance:11}];
+const dirs = fs.readdirSync('viewer/static/rooms').filter(d=>fs.statSync('viewer/static/rooms/'+d).isDirectory());
+async function genRoom(b64,j){
+ for(let a=1;a<=3;a++){
+ const r=await fetch('http://45.61.58.125:3075/api/generate-room',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({patternBase64:b64,roomType:j.roomType,angle:j.angle,cameraDistance:j.cameraDistance,patternWidth:36,patternHeight:36}),signal:AbortSignal.timeout(90000)});
+ const d=await r.json().catch(()=>({}));
+ if(d.success&&d.image) return Buffer.from(d.image.replace(/^data:[^,]+,/,''),'base64');
+ }
+ return null;
+}
+async function doSite(d){
+ const j=await (await fetch(`https://${d}/api/products?limit=100`,{signal:AbortSignal.timeout(12000)})).json();
+ const items=(j.items||j.products||[]).filter(p=>p&&p.image_url&&/^https/.test(p.image_url));
+ if(!items.length) return 'NOPROD';
+ const b=better(items, niche(d));
+ if(!b || b.image_url===items[0].image_url) return 'SAME';
+ const pat=Buffer.from(await(await fetch(b.image_url,{signal:AbortSignal.timeout(20000)})).arrayBuffer()).toString('base64');
+ const OUT='viewer/static/rooms/'+d;
+ const urls=[];
+ for(const room of ROOMS){
+ const buf=await genRoom(pat,room);
+ if(buf){const out=`${OUT}/${room.roomType}-${room.angle}.png`;fs.writeFileSync(out,buf);try{execFileSync('sips',['-Z','2368',out]);}catch{}urls.push(`/static/rooms/${d}/${room.roomType}-${room.angle}.png`);}
+ }
+ if(urls.length){await fetch('http://localhost:9774/api/hero-pool/set',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({domain:d,images:urls})});}
+ return 'REGEN('+urls.length+') <- '+(b.title||'').slice(0,40);
+}
+let i=0; const CONC=3; const results=[];
+async function w(){ while(i<dirs.length){ const d=dirs[i++]; try{ const r=await doSite(d); if(r.startsWith('REGEN')) console.log('OK '+d+' '+r); else results.push(d+':'+r);}catch(e){console.log('ERR '+d+' '+e.message.slice(0,40));} } }
+await Promise.all(Array.from({length:CONC},w));
+console.log('\ndone. unchanged:', results.length);
← 8a74fdf rooms: batch generator (all standalone sites) + deploy pushe
·
back to Dw Fleet Registry
·
livefleet viewer: add fleet Sign out (SSO logout) link a6f39b4 →