[object Object]

← back to Dw Marketing Reels

Add sync-reels predeploy guard: auto-refresh reels/ from freshest render before deploy (fixes stale-embed desync)

1d80d7377e24b6108dd6e134320c4e2a69289fad · 2026-08-10 12:05:44 -0700 · Steve Abrams

Files touched

Diff

commit 1d80d7377e24b6108dd6e134320c4e2a69289fad
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 10 12:05:44 2026 -0700

    Add sync-reels predeploy guard: auto-refresh reels/ from freshest render before deploy (fixes stale-embed desync)
---
 scripts/sync-reels.mjs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/scripts/sync-reels.mjs b/scripts/sync-reels.mjs
new file mode 100644
index 0000000..f31cd6d
--- /dev/null
+++ b/scripts/sync-reels.mjs
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+/**
+ * sync-reels.mjs — single-source-of-truth guard for the flip-book pipeline.
+ *
+ * WHY: the studio serves the public video from `reels/<slug>-flipbook.mp4`, but
+ * `build-page.mjs` only COPIES the render into `reels/` once, at page-build time.
+ * Any RE-RENDER after that writes to `flipbooks/<slug>/renders/<slug>-flipbook.mp4`
+ * and silently leaves `reels/` (the served file) stale — so the live page shows an
+ * old cut while "deploy OK" reports success. (Hit live on Majilite Metallics,
+ * 2026-08-10, TK-10413.)
+ *
+ * FIX: run this as the project's PREDEPLOY_HOOK. Before every deploy it copies each
+ * render into `reels/` whenever the render is newer or differs in size, so the
+ * served file can never lag the freshest render again. It only SYNCS (never gates);
+ * it exits non-zero only on a real IO failure.
+ */
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const PROJ = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
+const flipbooks = path.join(PROJ, 'flipbooks');
+const reelsDir = path.join(PROJ, 'reels');
+fs.mkdirSync(reelsDir, { recursive: true });
+
+let synced = 0, checked = 0;
+try {
+  const slugs = fs.existsSync(flipbooks)
+    ? fs.readdirSync(flipbooks, { withFileTypes: true }).filter(d => d.isDirectory()).map(d => d.name)
+    : [];
+  for (const slug of slugs) {
+    const render = path.join(flipbooks, slug, 'renders', `${slug}-flipbook.mp4`);
+    if (!fs.existsSync(render)) continue;               // no render for this slug — skip
+    checked++;
+    const served = path.join(reelsDir, `${slug}-flipbook.mp4`);
+    const src = fs.statSync(render);
+    const cur = fs.existsSync(served) ? fs.statSync(served) : null;
+    const stale = !cur || cur.size !== src.size || cur.mtimeMs < src.mtimeMs;
+    if (stale) {
+      fs.copyFileSync(render, served);
+      console.log(`  sync-reels: ${slug} → reels/ (${(src.size / 1e6).toFixed(1)} MB, render was newer)`);
+      synced++;
+    }
+  }
+  console.log(`  sync-reels: ${checked} render(s) checked, ${synced} refreshed, ${checked - synced} already current`);
+  process.exit(0);
+} catch (e) {
+  console.error(`  sync-reels: IO error — ${e.message}`);
+  process.exit(1);                                       // real failure aborts deploy (safer than shipping unknown state)
+}

← 305a06c Majilite flip book: real Replicate commercial rooms on verso  ·  back to Dw Marketing Reels  ·  chore: wire sync-reels predeploy hook + version bump (sessio 5e6973b →