Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions skills-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
"files": 2
},
"talking-head-recut": {
"hash": "d5c51342625c9952",
"files": 27
"hash": "1c7d859d214f78ea",
"files": 28
},
"website-to-video": {
"hash": "e8143700c50b7469",
Expand Down
12 changes: 12 additions & 0 deletions skills/talking-head-recut/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,15 @@ ffmpeg -y -i "$VIDEO_PATH" -c:v libx264 -crf 18 -g 30 -keyint_min 30 \
data-track-index="1"
></video>
</div>
<!-- Preserve the source program audio while the visual video stays muted. -->
<audio
id="source-audio"
src="input-video.mp4"
data-start="0"
data-duration="121.2"
data-track-index="10"
data-volume="1"
></audio>

<!-- Layer 2: each card-host sits at the bounds dictated by its layout. -->
<!-- IMPORTANT: every card-host MUST carry BOTH "card-host" and "clip" classes. -->
Expand Down Expand Up @@ -1162,6 +1171,9 @@ PRODUCER_BROWSER_GPU_MODE=hardware npx hyperframes render public \
```

`hyperframes render <dir>` reads `<dir>/index.html` and produces the MP4.
The canonical composition keeps the visual `<video>` muted and mounts the same
source as the root `#source-audio` track, so the rendered MP4 preserves the
talking-head audio without a manual remux.
The flag `PRODUCER_BROWSER_GPU_MODE=hardware` (or `--browser-gpu`) is
strongly recommended on macOS — software-only Chrome rendering times out
on most laptops.
Expand Down
13 changes: 13 additions & 0 deletions skills/talking-head-recut/media-contract.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";

const skill = readFileSync(new URL("./SKILL.md", import.meta.url), "utf8");

test("canonical composition preserves source audio as a root media track", () => {
assert.match(skill, /<video[\s\S]*?id="bg-video"[\s\S]*?muted[\s\S]*?<\/video>/);
assert.match(
skill,
/<\/div>\s*<!-- Preserve the source program audio[\s\S]*?<audio[\s\S]*?id="source-audio"[\s\S]*?src="input-video\.mp4"[\s\S]*?data-track-index="10"[\s\S]*?<\/audio>/,
);
});
Loading