Skip to content

Commit 924b98a

Browse files
authored
fix(captions): preserve zero plate grain in generated postfx (#3656)
1 parent 3bc46a8 commit 924b98a

3 files changed

Lines changed: 55 additions & 5 deletions

File tree

skills-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"source": "heygen-com/hyperframes",
33
"skills": {
44
"embedded-captions": {
5-
"hash": "1f29e0f6c77da4f5",
5+
"hash": "1677ab00946eb80c",
66
"files": 142
77
},
88
"faceless-explainer": {

skills/embedded-captions/scripts/make-theme.cjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8574,7 +8574,9 @@ if (!bodyInBg || fx.html || fx.js || setp.fgHtml) {
85748574
}
85758575

85768576
// _postfx.sh: plate reaction after the matte composite (subject+text move as one)
8577-
const P = HEROLESS ? { grain: (dna.plate || {}).grain || 5 } : dna.plate || {};
8577+
const P = HEROLESS ? { grain: dna.plate?.grain ?? 5 } : dna.plate || {};
8578+
const grain = P.grain ?? 5;
8579+
const noise = grain === 0 ? "" : `,noise=alls=${grain}:allf=t+u`;
85788580
// punchOffset: themes whose impact is NOT the hero onset (e.g. flapboard's
85798581
// lock-complete clack) shift the plate punch anchor; default keeps onset+2f
85808582
const anchorT = (heroIn + (P.punchOffset ?? 0.045)).toFixed(3);
@@ -8606,8 +8608,7 @@ fs.writeFileSync(
86068608
set -euo pipefail
86078609
cd "$(dirname "$0")"
86088610
ffmpeg -y -v error -i final.mp4 -filter_complex "
8609-
[0:v]${filter}${rgba},
8610-
noise=alls=${P.grain || 5}:allf=t+u[v]" \\
8611+
[0:v]${filter}${rgba}${noise}[v]" \\
86118612
-map "[v]" -map 0:a -c:v libx264 -crf 14 -preset slow -profile:v high -c:a copy \\
86128613
final_fx.mp4
86138614
echo "[postfx] ${dna.name} → final_fx.mp4"

skills/embedded-captions/scripts/make-theme.test.mjs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import assert from "node:assert/strict";
22
import { spawnSync } from "node:child_process";
3-
import { cpSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3+
import {
4+
cpSync,
5+
mkdirSync,
6+
mkdtempSync,
7+
readFileSync,
8+
readdirSync,
9+
rmSync,
10+
writeFileSync,
11+
} from "node:fs";
412
import { tmpdir } from "node:os";
513
import { dirname, join, resolve } from "node:path";
614
import test from "node:test";
@@ -80,3 +88,44 @@ test("every embedded-caption theme compiles a sane heroless body timeline", asyn
8088
});
8189
}
8290
});
91+
92+
for (const heroless of [false, true]) {
93+
for (const grain of [0, 3, undefined]) {
94+
test(`postfx preserves grain ${grain} with heroless=${heroless}`, (t) => {
95+
const workspace = mkdtempSync(join(tmpdir(), "embedded-captions-grain-"));
96+
t.after(() => rmSync(workspace, { recursive: true, force: true }));
97+
const project = join(workspace, "project");
98+
cpSync(fixturesDir, project, { recursive: true });
99+
mkdirSync(join(workspace, "scripts"));
100+
mkdirSync(join(workspace, "themes"));
101+
const compiler = join(workspace, "scripts", "make-theme.cjs");
102+
cpSync(makeTheme, compiler);
103+
const dna = JSON.parse(readFileSync(join(skillDir, "themes", "anchor.json"), "utf8"));
104+
dna.plate.grain = grain;
105+
writeFileSync(join(workspace, "themes", "anchor.json"), JSON.stringify(dna));
106+
writeFileSync(
107+
join(project, "theme.json"),
108+
JSON.stringify({
109+
...fixtureTheme,
110+
dna: "anchor",
111+
...(heroless
112+
? {}
113+
: {
114+
hero: { match: "hero" },
115+
lines: fixtureTheme.lines.map((line) => line.filter((word) => word !== "hero")),
116+
}),
117+
}),
118+
);
119+
const result = spawnSync(process.execPath, [compiler, project], {
120+
encoding: "utf8",
121+
timeout: 10_000,
122+
});
123+
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
124+
const postfx = readFileSync(join(project, "_postfx.sh"), "utf8");
125+
if (grain === 0) assert.doesNotMatch(postfx, /noise=/);
126+
else assert.match(postfx, new RegExp(`noise=alls=${grain ?? 5}:allf=t\\+u`));
127+
assert.doesNotMatch(postfx, /,\s*\[v\]/);
128+
assert.match(postfx, heroless ? /\[0:v\]null/ : /zoompan=/);
129+
});
130+
}
131+
}

0 commit comments

Comments
 (0)