← 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
D data/allnewsdaily-videos.jsonM lib/data.jsM public/css/site.cssM routes/public.jsD scripts/sync-allnewsdaily-videos.mjsM views/public/videos.ejs
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 →