← back to Dw Marketing Reels

scripts/sync-reels.mjs

51 lines

#!/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)
}