[object Object]

← back to Beverlyhillsvideos

auto-save: 2026-08-06T05:48:58 (1 files) — build.mjs

519176b55d10f87819e484e59ab151f566faf156 · 2026-08-06 05:49:01 -0700 · Steve Abrams

Files touched

Diff

commit 519176b55d10f87819e484e59ab151f566faf156
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 6 05:49:01 2026 -0700

    auto-save: 2026-08-06T05:48:58 (1 files) — build.mjs
---
 build.mjs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/build.mjs b/build.mjs
index 481c2d5..1cfe72e 100644
--- a/build.mjs
+++ b/build.mjs
@@ -397,6 +397,63 @@ writeFileSync('public/map.html', page({
   canonical: '/map.html', body: mapBody,
 }));
 
+// ---- per-restaurant film pages (SEO surface: film + cuisine + contact + menu) ----
+mkdirSync('public/restaurants', { recursive: true });
+let R_CUISINE = {}, R_SCRIPTS = {};
+try { (JSON.parse(readFileSync('data/restaurants.json','utf8')).restaurants||[]).forEach(r => R_CUISINE[r.name]=r.cuisine); } catch {}
+try { R_SCRIPTS = JSON.parse(readFileSync('data/film-scripts.json','utf8')); } catch {}
+const rnorm = s => (s||'').toLowerCase().replace(/beverly hills|estiatorio|the /g,'').replace(/[^a-z0-9]+/g,'').trim();
+const findBy = (obj, name) => { const n = rnorm(name); for (const k of Object.keys(obj)) { const kn = rnorm(k); if (kn && (kn===n || kn.includes(n) || n.includes(kn))) return obj[k]; } return null; };
+const contactFor = name => MAP_LOCS.filter(l=>l.type==='restaurant').find(l => { const a=rnorm(l.name), b=rnorm(name); return a===b || a.includes(b) || b.includes(a); });
+const restFilms = FILMS.map(f => {
+  const c = contactFor(f.name);
+  return { ...f,
+    cuisine: R_CUISINE[f.name] || findBy(R_CUISINE, f.name) || (c && c.category) || 'Beverly Hills',
+    narration: R_SCRIPTS[f.name] || findBy(R_SCRIPTS, f.name) || f.blurb,
+    address: c && c.address, phone: c && c.phone, menu_url: c && c.menu_url };
+});
+for (const f of restFilms) {
+  const tel = f.phone ? f.phone.replace(/[^+0-9]/g,'') : '';
+  const contact = (f.address || f.phone || f.menu_url) ? `
+      <div class="rest-contact">
+        ${f.address ? `<a class="paddr" href="https://maps.google.com/?q=${encodeURIComponent(f.name+' '+f.address)}" target="_blank" rel="noopener">${f.address}</a>` : ''}
+        ${f.phone ? `<a class="pcall" href="tel:${tel}">Call ${f.phone}</a>` : ''}
+        ${f.menu_url ? `<a class="pmenu" href="${f.menu_url}" target="_blank" rel="noopener">View current menu →</a>` : ''}
+      </div>` : '';
+  const body = `
+  <article class="rest"><div class="wrap">
+    <p class="crumb"><a href="/">Home</a> · <a href="/restaurants.html">Restaurants</a> · ${f.cuisine}</p>
+    <p class="eyebrow">${f.cuisine} · Beverly Hills</p>
+    <h1>${f.name.replace(/ Beverly Hills$/,'')}</h1>
+    <figure class="film rest-film"><video controls preload="metadata" playsinline poster="${f.poster}"><source src="${f.src}" type="video/mp4"></video></figure>
+    <p class="rest-narr">${f.narration}</p>
+    ${contact}
+    ${adSlot()}
+    <p class="rest-more"><a href="/map.html">See it on the map →</a> &nbsp;·&nbsp; <a href="/videos.html">All restaurant films →</a></p>
+  </div></article>`;
+  writeFileSync(`public/restaurants/${f.slug}.html`, page({
+    title: `${f.name.replace(/ Beverly Hills$/,'')} — Beverly Hills ${f.cuisine} | ${BRAND}`,
+    desc: f.narration.slice(0,155),
+    canonical: `/restaurants/${f.slug}.html`, body,
+  }));
+}
+// index
+const restIndex = `
+  <section class="hero home-hero vids-hero"><div class="wrap">
+    <p class="eyebrow">The Restaurants</p><h1>Every great table in Beverly Hills.</h1>
+    <p class="sub">Original short films on ${restFilms.length} of the city's finest restaurants — with menus, maps, and click-to-call.</p>
+  </div></section>
+  <section class="rest-grid-sec"><div class="wrap"><div class="rest-grid">
+    ${restFilms.map(f => `<a class="rest-card" href="/restaurants/${f.slug}.html">
+      <div class="rc-thumb"><img src="${f.poster}" alt="${f.name}" loading="lazy"></div>
+      <div class="rc-meta"><strong>${f.name.replace(/ Beverly Hills$/,'')}</strong><span>${f.cuisine}</span></div></a>`).join('')}
+  </div></div></section>`;
+writeFileSync('public/restaurants.html', page({
+  title: `Beverly Hills Restaurants — ${restFilms.length} Original Films, Menus & Map | ${BRAND}`,
+  desc: `Original short films on ${restFilms.length} of the finest restaurants in Beverly Hills, each with its current menu, address, and click-to-call.`,
+  canonical: '/restaurants.html', body: restIndex,
+}));
+
 // ---- privacy (AdSense-compliant) ----
 const privacyBody = `
   <article class="post">

← 80e43a0 films: fix CUT + Mastro's integration (had undefined slug ->  ·  back to Beverlyhillsvideos  ·  restaurants: 50 per-restaurant SEO film pages (embed film + bcb2964 →