[object Object]

← back to Rentv 2026

Content Studio: one-button Email-to-Boom now includes images + video links (rich send)

7d1be369f8c90ffd01dc9e74e036942acdc7b42b · 2026-08-05 11:16:25 -0700 · steve

Files touched

Diff

commit 7d1be369f8c90ffd01dc9e74e036942acdc7b42b
Author: steve <steve@designerwallcoverings.com>
Date:   Wed Aug 5 11:16:25 2026 -0700

    Content Studio: one-button Email-to-Boom now includes images + video links (rich send)
---
 public/admin/pr-intelligence/content-studio.html |  6 ++++--
 server.js                                        | 14 +++++++++++++-
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/public/admin/pr-intelligence/content-studio.html b/public/admin/pr-intelligence/content-studio.html
index 62050d2e..a1f4b90d 100644
--- a/public/admin/pr-intelligence/content-studio.html
+++ b/public/admin/pr-intelligence/content-studio.html
@@ -142,6 +142,7 @@
       <div class="cc" id="imgmeta"></div>
       <div id="imgcands" style="display:flex;flex-wrap:wrap;gap:8px;margin:0 0 10px"></div>
       <div class="cs-field"><label>Photo credit (the firm the image came from)</label><input id="b_credit" placeholder="e.g. CBRE"></div>
+      <div class="cs-field"><label>Video link(s) — included in the email to Boom (comma/newline separated)</label><input id="b_videos" placeholder="https://… reel / teaser / video URLs"></div>
       <div class="cs-field"><label>Body <a class="cs-copy" data-copy="b_body">📋 copy</a></label><textarea id="b_body" class="cs-body"></textarea></div>
       <div class="cs-row">
         <button class="btn" id="publish">🚀 Publish to blog</button>
@@ -169,7 +170,7 @@
     const srcEl = document.getElementById('src');
     gen.disabled = false; gen.textContent = '✨ Pull highlights + draft';
     $('b_image').value = selected.image ? (selected.image.startsWith('http') ? selected.image : location.origin + selected.image) : '';
-    $('b_credit').value = ''; $('imgcands').innerHTML = ''; $('imgmeta').innerHTML = '';
+    $('b_credit').value = ''; $('b_videos').value = ''; $('imgcands').innerHTML = ''; $('imgmeta').innerHTML = '';
     // RENTV stories (numeric id) get a full-body fetch; external sources use their RSS summary.
     const isRentv = /^\d+$/.test(String(selected.id)) && src === 'RENTV';
     if (isRentv) {
@@ -277,7 +278,8 @@
     try {
       const r = await PR.api('/../content/email', { method: 'POST', body: {
         title: $('b_title').value.trim(), dek: $('b_dek').value.trim(), image: $('b_image').value.trim(),
-        photo_credit: $('b_credit').value.trim(), body: $('b_body').value, linkedin: $('s_li').value, x: $('s_x').value,
+        photo_credit: $('b_credit').value.trim(), videos: $('b_videos').value, body: $('b_body').value,
+        linkedin: $('s_li').value, x: $('s_x').value,
       } });
       $('pubmeta').innerHTML = `<span class="cs-ok">✅ Emailed to ${PR.esc(r.to)}</span>`;
       PR.toast('Emailed to Boom');
diff --git a/server.js b/server.js
index 87ca2ff2..d8d7510d 100644
--- a/server.js
+++ b/server.js
@@ -511,9 +511,21 @@ app.post('/api/content/email', adminOnly, async (req, res) => {
   const b = req.body || {};
   if (!GEORGE_HDR) return res.status(503).json({ ok: false, error: 'email not configured (GEORGE_BASIC_AUTH missing)' });
   const title = clean(b.title, 200) || 'RENTV post';
+  // Collect ALL images (the credited building photo + any extras) and ALL videos — one rich send.
+  const images = (Array.isArray(b.images) ? b.images : String(b.images || '').split(/[\n,]+/)).map(s => clean(s, 500)).filter(u => /^https?:|^\/img\//i.test(u));
+  if (b.image && !images.includes(clean(b.image, 500))) images.unshift(clean(b.image, 500));
+  const videos = (Array.isArray(b.videos) ? b.videos : String(b.videos || '').split(/[\n,]+/)).map(s => clean(s, 500)).filter(u => /^https?:|^\/(vid|social-videos)\//i.test(u));
+  const absolutize = (u) => /^https?:/i.test(u) ? u : ('https://rentv.agentabrams.com' + u);
+  const imgHtml = images.length
+    ? `<p>${images.map(u => `<img src="${absolutize(u)}" style="max-width:100%;margin-bottom:6px">`).join('<br>')}<br><small>Photo: ${clean(b.photo_credit, 160) || '—'}</small></p>`
+    : '';
+  const vidHtml = videos.length
+    ? `<hr><h3>Video${videos.length > 1 ? 's' : ''}</h3>` + videos.map(u => `<p>▶ <a href="${absolutize(u)}">${absolutize(u)}</a></p>`).join('')
+    : '';
   const html = `<h1>${clean(b.title, 200)}</h1>${b.dek ? `<p><em>${clean(b.dek, 300)}</em></p>` : ''}`
-    + (b.image ? `<p><img src="${clean(b.image, 500)}" style="max-width:100%"><br><small>Photo: ${clean(b.photo_credit, 160) || '—'}</small></p>` : '')
+    + imgHtml
     + `<div>${String(b.body || '').split(/\n\n+/).map(p => `<p>${clean(p, 4000)}</p>`).join('')}</div>`
+    + vidHtml
     + (b.linkedin ? `<hr><h3>LinkedIn</h3><p>${clean(b.linkedin, 3000)}</p>` : '')
     + (b.x ? `<h3>X</h3><p>${clean(b.x, 600)}</p>` : '');
   try {

← c0aff668 auto-save: 2026-08-05T11:11:27 (3 files) — data/markets.json  ·  back to Rentv 2026  ·  chore: v0.19.0 (session close — Content Studio + photo track 3cdd23d7 →