Setting the file. One moment.
Audio Test · Product Launch Video · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page scripts/ audio.test.mjs
JavaScript · 234 lines · 10 KB
script
=
new
URL
(
"./audio.mjs"
,
import
.
meta
.url).pathname;
9
10 function runAudio ({ args = [], env = {} } = {}) {
11 const dir = mkdtempSync ( join ( tmpdir (), "product-launch-audio-" ));
12 const engine = join (dir, "engine.mjs" );
13 writeFileSync ( join (dir, "STORYBOARD.md" ), "message: Test \n " );
14 writeFileSync (
15 engine,
16 `import { readFileSync, writeFileSync } from "node:fs";
17 const argv = process.argv.slice(2);
18 const flag = (name) => argv[argv.indexOf(name) + 1];
19 const request = JSON.parse(readFileSync(flag("--request"), "utf8"));
20 writeFileSync(new URL("request.json", import.meta.url), JSON.stringify(request));
21 writeFileSync(flag("--out"), JSON.stringify({ voices: [], bgm: null, sfx: [] }));
22 ` ,
23 );
24 const result = spawnSync (
25 process.execPath,
26 [script, "--hyperframes" , dir, "--storyboard" , join (dir, "STORYBOARD.md" ), ... args],
27 { encoding: "utf8" , env: { ... process.env, HF_MEDIA_ENGINE: engine, ... env } },
28 );
29 assert. equal (result.status, 0 , result.stderr);
30 return JSON . parse ( readFileSync ( join (dir, "request.json" ), "utf8" ));
31 }
32
33 test ( "passes --provider to the shared audio engine" , () => {
34 assert. equal ( runAudio ({ args: [ "--provider" , "kokoro" ] }).provider, "kokoro" );
35 });
36
37 test ( "passes Gemini model and delivery style through the workflow adapter" , () => {
38 const request = runAudio ({
39 args: [
40 "--provider" ,
41 "gemini" ,
42 "--tts-model" ,
43 "gemini-3.8-flash-lite-tts" ,
44 "--style" ,
45 "Warm and calm" ,
46 ],
47 });
48 assert. equal (request.provider, "gemini" );
49 assert. equal (request.tts_model, "gemini-3.8-flash-lite-tts" );
50 assert. equal (request.style, "Warm and calm" );
51 });
52
53 test ( "uses HF_TTS_PROVIDER when --provider is omitted" , () => {
54 assert. equal ( runAudio ({ env: { HF_TTS_PROVIDER: "elevenlabs" } }).provider, "elevenlabs" );
55 });
56
57 test ( "--provider takes precedence over HF_TTS_PROVIDER" , () => {
58 assert. equal (
59 runAudio ({ args: [ "--provider" , "kokoro" ], env: { HF_TTS_PROVIDER: "elevenlabs" } }).provider,
60 "kokoro" ,
61 );
62 });
63
64 // ── the canonical fully-silent marker (SKILL.md Step 3.1) ────────────────────
65 // `music: none` in the storyboard's top YAML block + no SCRIPT.md marks the
66 // project fully silent: generate must produce nothing (an absent
67 // audio_meta.json is what assemble treats as silent) and clear stale meta.
68
69 /** Like runAudio, but with a caller-controlled storyboard and no request assertion. */
70 function runAudioRaw ({ storyboard , scriptMd = null , preexistingMeta = null }) {
71 const dir = mkdtempSync ( join ( tmpdir (), "product-launch-audio-" ));
72 const engine = join (dir, "engine.mjs" );
73 writeFileSync ( join (dir, "STORYBOARD.md" ), storyboard);
74 if (scriptMd != null ) writeFileSync ( join (dir, "SCRIPT.md" ), scriptMd);
75 if (preexistingMeta != null ) writeFileSync ( join (dir, "audio_meta.json" ), preexistingMeta);
76 writeFileSync (
77 engine,
78 `import { readFileSync, writeFileSync } from "node:fs";
79 const argv = process.argv.slice(2);
80 const flag = (name) => argv[argv.indexOf(name) + 1];
81 const request = JSON.parse(readFileSync(flag("--request"), "utf8"));
82 writeFileSync(new URL("request.json", import.meta.url), JSON.stringify(request));
83 writeFileSync(flag("--out"), JSON.stringify({ voices: [], bgm: null, sfx: [] }));
84 ` ,
85 );
86 const result = spawnSync (
87 process.execPath,
88 [script, "--hyperframes" , dir, "--storyboard" , join (dir, "STORYBOARD.md" )],
89 { encoding: "utf8" , env: { ... process.env, HF_MEDIA_ENGINE: engine } },
90 );
91 return { dir, result };
92 }
93
94 test ( "music: none + no SCRIPT.md = fully silent: no engine run, no audio_meta.json" , () => {
95 const { dir , result } = runAudioRaw ({ storyboard: "--- \n message: Test \n music: none \n --- \n " });
96
97 assert. equal (result.status, 0 , result.stderr);
98 assert. match (result.stdout, /marked silent/ );
99 // The engine was never invoked...
100 assert. equal ( existsSync ( join (dir, "request.json" )), false );
101 // ...and no meta exists (absence ⇒ assemble treats the film as silent).
102 assert. equal ( existsSync ( join (dir, "audio_meta.json" )), false );
103 });
104
105 test ( "fully-silent run removes stale audio_meta.json from a previous non-silent run" , () => {
106 const { dir , result } = runAudioRaw ({
107 storyboard: "--- \n message: Test \n music: none \n --- \n " ,
108 preexistingMeta: JSON . stringify ({ bgm: { path: "old.mp3" }, voices: [], sfx: [] }),
109 });
110
111 assert. equal (result.status, 0 , result.stderr);
112 assert. equal ( existsSync ( join (dir, "audio_meta.json" )), false );
113 });
114
115 test ( "music: none with narration keeps TTS but turns BGM off (not fully silent)" , () => {
116 const { dir , result } = runAudioRaw ({
117 storyboard: "--- \n message: Test \n music: none \n --- \n " ,
118 scriptMd: "## Hook (Frame 1) \n\n Spoken line for frame one. \n " ,
119 });
120
121 assert. equal (result.status, 0 , result.stderr);
122 const request = JSON . parse ( readFileSync ( join (dir, "request.json" ), "utf8" ));
123 assert. equal (request.bgm.mode, "none" );
124 assert. equal (request.lines. length , 1 );
125 });
126
127 test ( 'a quoted music: "none" is still the silent marker (frontmatter stripQuotes)' , () => {
128 // YAML authors quote scalars freely; the vendored storyboard parser strips
129 // matching quotes at parse time (storyboard.mjs stripQuotes), so the marker
130 // must not depend on the unquoted spelling.
131 const { dir , result } = runAudioRaw ({
132 storyboard: '--- \n message: Test \n music: "none" \n --- \n ' ,
133 });
134
135 assert. equal (result.status, 0 , result.stderr);
136 assert. match (result.stdout, /marked silent/ );
137 assert. equal ( existsSync ( join (dir, "audio_meta.json" )), false );
138 });
139
140 test ( "a storyboard music mood still retrieves BGM (marker is exact, not fuzzy)" , () => {
141 const { dir , result } = runAudioRaw ({
142 storyboard: "--- \n message: Test \n music: upbeat synthwave with heavy drums \n --- \n " ,
143 });
144
145 assert. equal (result.status, 0 , result.stderr);
146 const request = JSON . parse ( readFileSync ( join (dir, "request.json" ), "utf8" ));
147 assert. equal (request.bgm.mode, "retrieve" );
148 assert. equal (request.bgm.query, "upbeat synthwave with heavy drums" );
149 });
150
151 // ── fetch-sfx ────────────────────────────────────────────────────────────────
152 // Regressions found while running the full product-launch workflow end to end
153 // (linear.app site showcase, 2026-07-30).
154
155 /** Runs the fetch-sfx subcommand. `neutralOut` is what the stub engine writes to --out. */
156 function runFetchSfx ({ storyboard , neutralOut = { voices: [], bgm: null , sfx: [] } }) {
157 const dir = mkdtempSync ( join ( tmpdir (), "product-launch-sfx-" ));
158 const engine = join (dir, "engine.mjs" );
159 writeFileSync ( join (dir, "STORYBOARD.md" ), storyboard);
160 writeFileSync (
161 engine,
162 `import { readFileSync, writeFileSync } from "node:fs";
163 const argv = process.argv.slice(2);
164 const flag = (name) => argv[argv.indexOf(name) + 1];
165 const request = JSON.parse(readFileSync(flag("--request"), "utf8"));
166 writeFileSync(new URL("request.json", import.meta.url), JSON.stringify(request));
167 writeFileSync(flag("--out"), ${ JSON . stringify ( JSON . stringify ( neutralOut )) });
168 ` ,
169 );
170 const result = spawnSync (
171 process.execPath,
172 [script, "fetch-sfx" , "--hyperframes" , dir, "--storyboard" , join (dir, "STORYBOARD.md" )],
173 { encoding: "utf8" , env: { ... process.env, HF_MEDIA_ENGINE: engine } },
174 );
175 return { dir, result };
176 }
177
178 const FRAME_WITH_SFX = ( sfx ) =>
179 `--- \n message: Test \n --- \n\n ## Frame 1 — Hook \n - duration: 3s \n - sfx: ${ sfx } \n ` ;
180
181 test ( "fetch-sfx: `sfx: none` is an absence marker, not a cue named none" , () => {
182 const { dir , result } = runFetchSfx ({ storyboard: FRAME_WITH_SFX ( "none" ) });
183
184 assert. equal (result.status, 0 , result.stderr);
185 const request = JSON . parse ( readFileSync ( join (dir, "request.json" ), "utf8" ));
186 // Used to reach the engine as { sfx: ["none"] } — a cue that cannot resolve.
187 assert. deepEqual (request.lines, []);
188 });
189
190 test ( "fetch-sfx: the other absence spellings are markers too" , () => {
191 for ( const spelling of [ "None" , "n/a" , "NA" , "skip" , "-" , "—" ]) {
192 const { dir , result } = runFetchSfx ({ storyboard: FRAME_WITH_SFX (spelling) });
193 assert. equal (result.status, 0 , result.stderr);
194 const request = JSON . parse ( readFileSync ( join (dir, "request.json" ), "utf8" ));
195 assert. deepEqual (request.lines, [], `spelling: ${ spelling }` );
196 }
197 });
198
199 test ( "fetch-sfx: a real cue still reaches the engine, and mixed lists drop only the marker" , () => {
200 const { dir , result } = runFetchSfx ({ storyboard: FRAME_WITH_SFX ( "whoosh, none, click" ) });
201
202 assert. equal (result.status, 0 , result.stderr);
203 const request = JSON . parse ( readFileSync ( join (dir, "request.json" ), "utf8" ));
204 assert. deepEqual (request.lines, [{ id: "01" , sfx: [ "whoosh" , "click" ] }]);
205 });
206
207 test ( "fetch-sfx: carries bgm_pending through and warns that the snapshot has no bed" , () => {
208 const { dir , result } = runFetchSfx ({
209 storyboard: FRAME_WITH_SFX ( "whoosh" ),
210 // A detached Lyria/MusicGen generate that has not landed yet.
211 neutralOut: { voices: [], bgm: null , bgm_pending: true , sfx: [] },
212 });
213
214 assert. equal (result.status, 0 , result.stderr);
215 const meta = JSON . parse ( readFileSync ( join (dir, "audio_meta.json" ), "utf8" ));
216 // The flag used to be dropped in the neutral → PL translation, making "not ready yet"
217 // indistinguishable from "silent by design".
218 assert. equal (meta.bgm_pending, true );
219 assert. equal (meta.bgm, null );
220 assert. match (result.stderr + result.stdout, /pending/ i );
221 });
222
223 test ( "fetch-sfx: a resolved bed reports bgm_pending false and no warning" , () => {
224 const { dir , result } = runFetchSfx ({
225 storyboard: FRAME_WITH_SFX ( "whoosh" ),
226 neutralOut: { voices: [], bgm: { path: "assets/bgm/track.mp3" , volume: 0.12 }, sfx: [] },
227 });
228
229 assert. equal (result.status, 0 , result.stderr);
230 const meta = JSON . parse ( readFileSync ( join (dir, "audio_meta.json" ), "utf8" ));
231 assert. equal (meta.bgm_pending, false );
232 assert. equal (meta.bgm.path, "assets/bgm/track.mp3" );
233 assert. doesNotMatch (result.stderr, /pending/ i );
234 });