[object Object]

← back to Stars of Design

Revert "Add auto-updating All News Daily embedded Shorts section to /videos"

2297cd967519e691038dddea401062b6efe89621 · 2026-09-09 23:57:21 -0700 · Steve Abrams

This reverts commit 8ffbfd96fa23bb3890e0ee80c823fc22b3ef5c63.

Files touched

Diff

commit 2297cd967519e691038dddea401062b6efe89621
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 23:57:21 2026 -0700

    Revert "Add auto-updating All News Daily embedded Shorts section to /videos"
    
    This reverts commit 8ffbfd96fa23bb3890e0ee80c823fc22b3ef5c63.
---
 data/allnewsdaily-videos.json        |   7 --
 lib/data.js                          |  32 ------
 public/css/site.css                  |  35 -------
 routes/public.js                     |   3 -
 scripts/sync-allnewsdaily-videos.mjs | 197 -----------------------------------
 views/public/videos.ejs              |  37 -------
 6 files changed, 311 deletions(-)

diff --git a/data/allnewsdaily-videos.json b/data/allnewsdaily-videos.json
deleted file mode 100644
index 5d1959d..0000000
--- a/data/allnewsdaily-videos.json
+++ /dev/null
@@ -1,7 +0,0 @@
-[
-  {
-    "videoId": "JRcJ5SInd48",
-    "title": "All News Daily — September 10: Trump claims he will provide $5,000 to each American adul…",
-    "publishedAt": "2026-09-10T05:45:17Z"
-  }
-]
diff --git a/lib/data.js b/lib/data.js
index 834dbe1..f9ce32d 100644
--- a/lib/data.js
+++ b/lib/data.js
@@ -142,39 +142,7 @@ function filterVideos(opts = {}) {
   return rows;
 }
 
-// ── All News Daily: embedded YouTube Shorts (data/allnewsdaily-videos.json) ──
-// Written by scripts/sync-allnewsdaily-videos.mjs — [{videoId, title, publishedAt}].
-// Guard against a missing file (returns []) and re-derive the embed URL from the
-// video id to be defensive — never trust the raw JSON id for the iframe src.
-const ALLNEWS_FILE = path.join(__dirname, '..', 'data', 'allnewsdaily-videos.json');
-let _allNews = null;
-let _allNewsMtime = 0;
-
-function loadAllNewsDaily() {
-  try {
-    const stat = fs.statSync(ALLNEWS_FILE);
-    if (!_allNews || stat.mtimeMs !== _allNewsMtime) {
-      const raw = JSON.parse(fs.readFileSync(ALLNEWS_FILE, 'utf8'));
-      _allNews = (Array.isArray(raw) ? raw : [])
-        .filter(v => v && /^[A-Za-z0-9_-]{11}$/.test(v.videoId))
-        .map(v => ({
-          videoId: v.videoId,
-          title: v.title || '',
-          publishedAt: v.publishedAt || '',
-          embed_url: `https://www.youtube.com/embed/${v.videoId}`,
-          watch_url: `https://www.youtube.com/watch?v=${v.videoId}`,
-        }));
-      _allNewsMtime = stat.mtimeMs;
-    }
-  } catch (e) {
-    if (e.code === 'ENOENT') return [];
-    return [];  // best-effort: a malformed file should never break /videos
-  }
-  return _allNews || [];
-}
-
 module.exports = {
   load, bySlug, filter, allStyles, allEras,
   loadVideos, videosByDesigner, videoCategories, videoSources, filterVideos,
-  loadAllNewsDaily,
 };
diff --git a/public/css/site.css b/public/css/site.css
index 076ae61..736d88b 100644
--- a/public/css/site.css
+++ b/public/css/site.css
@@ -394,41 +394,6 @@ html[data-theme='dark']  .theme-toggle-sun  { display: none; }
 .sources li a, .links li a { color: var(--link); }
 .sources li em { color: var(--text-muted); font-style: italic; }
 
-/* All News Daily — embedded 9:16 Shorts section (top of /videos) */
-.and-section { border-bottom: 1px solid var(--border); }
-.and-grid {
-  display: grid;
-  /* Shorts are vertical (9:16) — narrower cards so the tall players tile nicely */
-  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
-  gap: 1.25rem;
-}
-.and-card {
-  background: var(--surface);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  overflow: hidden;
-  box-shadow: var(--shadow);
-  display: flex; flex-direction: column;
-}
-.and-embed {
-  position: relative;
-  aspect-ratio: 9 / 16;      /* vertical Short */
-  background: #0a0a0a;
-}
-.and-embed iframe {
-  position: absolute; inset: 0;
-  width: 100%; height: 100%;
-  border: 0;
-}
-.and-card-body { padding: .8rem 1rem 1rem; flex: 1; display: flex; flex-direction: column; gap: .25rem; }
-.and-card-body h3 {
-  font-family: var(--font-serif); font-size: .98rem; line-height: 1.3;
-  margin: 0; font-weight: 500;
-  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
-  overflow: hidden; text-overflow: ellipsis;
-}
-.and-card-body h3 a { color: var(--text); }
-
 /* Video gallery — /videos page */
 .video-grid {
   display: grid;
diff --git a/routes/public.js b/routes/public.js
index 5308168..e1bbb0c 100644
--- a/routes/public.js
+++ b/routes/public.js
@@ -649,8 +649,6 @@ router.get('/videos', (req, res, next) => {
       q:          req.query.q,
     };
     const rows = data.filterVideos(filt);
-    let allNewsDaily = [];
-    try { allNewsDaily = data.loadAllNewsDaily(); } catch (_) { allNewsDaily = []; }
     res.render('public/videos', {
       title: 'Designer Videos — Home Tours, Interviews & Project Showcases | Stars of Design',
       meta_desc_override: 'Curated YouTube video gallery of US interior designer home tours, sit-down interviews, and project showcases from Architectural Digest, House Beautiful, Studio McGee, and the firms themselves. Cross-linked to the Stars of Design directory.',
@@ -659,7 +657,6 @@ router.get('/videos', (req, res, next) => {
       filter: filt,
       categories: data.videoCategories(),
       sources:    data.videoSources(),
-      allNewsDaily,
     });
   } catch (e) { next(e); }
 });
diff --git a/scripts/sync-allnewsdaily-videos.mjs b/scripts/sync-allnewsdaily-videos.mjs
deleted file mode 100644
index 93cbd13..0000000
--- a/scripts/sync-allnewsdaily-videos.mjs
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/env node
-// sync-allnewsdaily-videos.mjs — pull the All News Daily YouTube channel's uploads
-// and write data/allnewsdaily-videos.json for the starsofdesign /videos page.
-//
-// Node v26, raw fetch, NO googleapis dependency.
-//
-// Flow:
-//   1. Refresh an access token (POST https://oauth2.googleapis.com/token)
-//        - refresh token   : ~/Projects/allnewsdaily/.env         (YOUTUBE_REFRESH_TOKEN=)
-//        - client id/secret: ~/Projects/secrets-manager/.env       (YOUTUBE_CLIENT_ID / YOUTUBE_CLIENT_SECRET)
-//   2. channels.list?part=contentDetails&mine=true → relatedPlaylists.uploads
-//   3. Page playlistItems?part=snippet over the uploads playlist
-//   4. FILTER to our Shorts only: snippet.title starts with "All News Daily —"
-//   5. Write [{videoId, title, publishedAt}] newest-first, cap ~24, to
-//        ~/Projects/starsofdesign/data/allnewsdaily-videos.json
-//
-// Best-effort: if the API fails, leave any existing allnewsdaily-videos.json
-// intact; if none exists, write [].
-//
-// Usage: node scripts/sync-allnewsdaily-videos.mjs
-
-import { readFileSync, writeFileSync, existsSync } from 'node:fs';
-import { homedir } from 'node:os';
-import { join, dirname } from 'node:path';
-import { fileURLToPath } from 'node:url';
-
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-const SECRETS_ENV = join(homedir(), 'Projects', 'secrets-manager', '.env');
-const ALLNEWS_ENV = join(homedir(), 'Projects', 'allnewsdaily', '.env');
-const OUT_FILE = join(__dirname, '..', 'data', 'allnewsdaily-videos.json');
-
-const CHANNEL_ID = 'UCr2NrTNDW_iDWvpNX0Txprg';   // All News Daily (informational; we resolve uploads via mine=true)
-const TITLE_PREFIX = 'All News Daily —';         // our upload-title format → keeps this section unique to our Shorts
-const CAP = 24;
-
-const TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token';
-
-// --- tiny .env parser (no dotenv dependency) -------------------------------
-function parseEnv(path) {
-  const out = {};
-  if (!existsSync(path)) return out;
-  const txt = readFileSync(path, 'utf8');
-  for (const raw of txt.split('\n')) {
-    const line = raw.trim();
-    if (!line || line.startsWith('#')) continue;
-    const eq = line.indexOf('=');
-    if (eq === -1) continue;
-    const key = line.slice(0, eq).trim();
-    let val = line.slice(eq + 1).trim();
-    if (
-      (val.startsWith('"') && val.endsWith('"')) ||
-      (val.startsWith("'") && val.endsWith("'"))
-    ) {
-      val = val.slice(1, -1);
-    }
-    out[key] = val;
-  }
-  return out;
-}
-
-function loadCreds() {
-  const secrets = parseEnv(SECRETS_ENV);
-  const app = parseEnv(ALLNEWS_ENV);
-  const clientId = secrets.YOUTUBE_CLIENT_ID || process.env.YOUTUBE_CLIENT_ID;
-  const clientSecret = secrets.YOUTUBE_CLIENT_SECRET || process.env.YOUTUBE_CLIENT_SECRET;
-  const refreshToken = app.YOUTUBE_REFRESH_TOKEN || process.env.YOUTUBE_REFRESH_TOKEN;
-  return { clientId, clientSecret, refreshToken };
-}
-
-async function refreshAccessToken({ clientId, clientSecret, refreshToken }) {
-  const body = new URLSearchParams({
-    client_id: clientId,
-    client_secret: clientSecret,
-    refresh_token: refreshToken,
-    grant_type: 'refresh_token',
-  });
-  const res = await fetch(TOKEN_ENDPOINT, {
-    method: 'POST',
-    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
-    body: body.toString(),
-  });
-  const json = await res.json().catch(() => ({}));
-  if (!res.ok || !json.access_token) {
-    throw new Error(
-      `token refresh HTTP ${res.status}: ${json.error || ''} ${json.error_description || ''}`.trim()
-    );
-  }
-  return json.access_token;
-}
-
-async function ytGet(url, accessToken) {
-  const res = await fetch(url, { headers: { Authorization: `Bearer ${accessToken}` } });
-  const json = await res.json().catch(() => ({}));
-  if (!res.ok) {
-    throw new Error(`GET ${url} → HTTP ${res.status}: ${JSON.stringify(json).slice(0, 300)}`);
-  }
-  return json;
-}
-
-async function getUploadsPlaylistId(accessToken) {
-  const json = await ytGet(
-    'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&mine=true',
-    accessToken
-  );
-  const item = json.items && json.items[0];
-  const uploads = item?.contentDetails?.relatedPlaylists?.uploads;
-  if (!uploads) throw new Error('no uploads playlist found for authorized channel');
-  return uploads;
-}
-
-async function getAllUploads(playlistId, accessToken) {
-  const items = [];
-  let pageToken = '';
-  // Page until exhausted OR we've comfortably collected enough matches.
-  for (let guard = 0; guard < 20; guard++) {
-    const url = new URL('https://www.googleapis.com/youtube/v3/playlistItems');
-    url.searchParams.set('part', 'snippet');
-    url.searchParams.set('playlistId', playlistId);
-    url.searchParams.set('maxResults', '50');
-    if (pageToken) url.searchParams.set('pageToken', pageToken);
-    const json = await ytGet(url.toString(), accessToken);
-    for (const it of json.items || []) {
-      const s = it.snippet || {};
-      const videoId = s.resourceId?.videoId;
-      if (!videoId) continue;
-      items.push({ videoId, title: s.title || '', publishedAt: s.publishedAt || '' });
-    }
-    pageToken = json.nextPageToken || '';
-    if (!pageToken) break;
-  }
-  return items;
-}
-
-function readExisting() {
-  try {
-    if (!existsSync(OUT_FILE)) return null;
-    const parsed = JSON.parse(readFileSync(OUT_FILE, 'utf8'));
-    return Array.isArray(parsed) ? parsed : null;
-  } catch (_) {
-    return null;
-  }
-}
-
-async function main() {
-  const creds = loadCreds();
-  if (!creds.clientId || !creds.clientSecret) {
-    console.error(`[sync-and] Missing YOUTUBE_CLIENT_ID / YOUTUBE_CLIENT_SECRET in ${SECRETS_ENV}`);
-    return bestEffortExit();
-  }
-  if (!creds.refreshToken) {
-    console.error(`[sync-and] Missing YOUTUBE_REFRESH_TOKEN in ${ALLNEWS_ENV}`);
-    return bestEffortExit();
-  }
-
-  try {
-    const accessToken = await refreshAccessToken(creds);
-    const uploadsPlaylist = await getUploadsPlaylistId(accessToken);
-    const all = await getAllUploads(uploadsPlaylist, accessToken);
-
-    // Filter to OUR Shorts (title prefix), newest-first, cap.
-    const filtered = all
-      .filter(v => v.title.startsWith(TITLE_PREFIX))
-      .sort((a, b) => (b.publishedAt || '').localeCompare(a.publishedAt || ''))
-      .slice(0, CAP)
-      .map(v => ({ videoId: v.videoId, title: v.title, publishedAt: v.publishedAt }));
-
-    console.log(
-      `[sync-and] channel uploads: ${all.length}, matching "${TITLE_PREFIX}": ${filtered.length} (cap ${CAP})`
-    );
-
-    writeFileSync(OUT_FILE, JSON.stringify(filtered, null, 2) + '\n');
-    console.log(`[sync-and] wrote ${filtered.length} video(s) → ${OUT_FILE}`);
-    process.exit(0);
-  } catch (e) {
-    console.error(`[sync-and] API failed: ${e.message}`);
-    return bestEffortExit();
-  }
-}
-
-// Best-effort: preserve any existing file; if none, write [] so the template
-// has something well-formed to load.
-function bestEffortExit() {
-  const existing = readExisting();
-  if (existing) {
-    console.error(`[sync-and] leaving existing ${OUT_FILE} intact (${existing.length} video(s))`);
-    process.exit(1);
-  }
-  writeFileSync(OUT_FILE, '[]\n');
-  console.error(`[sync-and] no existing file — wrote empty [] → ${OUT_FILE}`);
-  process.exit(1);
-}
-
-main().catch((e) => {
-  console.error(`[sync-and] Fatal: ${e.message}`);
-  bestEffortExit();
-});
diff --git a/views/public/videos.ejs b/views/public/videos.ejs
index 2136d04..cfe5f35 100644
--- a/views/public/videos.ejs
+++ b/views/public/videos.ejs
@@ -2,43 +2,6 @@
 <%- include('../partials/header') %>
 
 <main class="videos-page">
-  <% var andVideos = (typeof allNewsDaily !== 'undefined' && Array.isArray(allNewsDaily)) ? allNewsDaily : []; %>
-  <% if (andVideos.length) { %>
-  <section class="section and-section">
-    <div class="wrap">
-      <header class="rail-header" style="display:block">
-        <h1>All News Daily</h1>
-        <p class="muted" style="max-width:760px;margin:.5rem 0 1.25rem">
-          The latest short-form news briefings from the <strong>All News Daily</strong> channel — daily headline recaps, embedded and playable right here. New Shorts appear automatically as they publish.
-        </p>
-      </header>
-
-      <div class="and-grid">
-        <% andVideos.forEach(function (v) { %>
-          <article class="and-card">
-            <div class="and-embed">
-              <iframe
-                src="<%= v.embed_url %>"
-                title="<%= v.title %>"
-                loading="lazy"
-                frameborder="0"
-                allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
-                referrerpolicy="strict-origin-when-cross-origin"
-                allowfullscreen></iframe>
-            </div>
-            <div class="and-card-body">
-              <h3><a href="<%= v.watch_url %>" target="_blank" rel="noopener noreferrer"><%= v.title %></a></h3>
-              <% if (v.publishedAt) { %>
-                <p class="muted small"><%= new Date(v.publishedAt).toLocaleDateString('en-US', { year:'numeric', month:'short', day:'numeric' }) %></p>
-              <% } %>
-            </div>
-          </article>
-        <% }); %>
-      </div>
-    </div>
-  </section>
-  <% } %>
-
   <section class="section">
     <div class="wrap">
       <header class="rail-header" style="display:block">

← 2dd03c4 Revert "Mac2 sync-and-push job for the /videos All News Dail  ·  back to Stars of Design  ·  Allow AdSense through public CSP 32994ed →