8602c70eea
Capture pipeline (Playwright recordVideo + injected synthetic cursor + ffmpeg webm->mp4) and a Remotion LiveFilm composition driven by a 20-clip manifest with caption/zoom overlays and a music bed. Includes the capture orchestrator, storm-intel reference scene, pre-render smoke check, and VO script. Pure logic is unit-tested (28 passing). Remaining scene scripts are authored during live capture.
33 lines
1.1 KiB
TypeScript
33 lines
1.1 KiB
TypeScript
import { Series, AbsoluteFill } from "remotion";
|
|
import manifest from "./clips.manifest.json";
|
|
import { totalLiveFrames, type ClipsManifest } from "./clips";
|
|
import { DURATIONS } from "./timing";
|
|
import { ClipStage } from "./components/ClipStage";
|
|
import { AudioTrack } from "./components/AudioTrack";
|
|
import { Promise as PromiseSeq } from "./sequences/Promise";
|
|
|
|
const m = manifest as ClipsManifest;
|
|
export const LIVE_TOTAL = totalLiveFrames(m) + DURATIONS.promise;
|
|
|
|
export const LiveFilm: React.FC = () => {
|
|
const [hero, ...rest] = m.clips; // landing-hero is first in the manifest
|
|
return (
|
|
<AbsoluteFill>
|
|
<AudioTrack totalFrames={LIVE_TOTAL} />
|
|
<Series>
|
|
<Series.Sequence durationInFrames={hero.durationFrames}>
|
|
<ClipStage clip={hero} />
|
|
</Series.Sequence>
|
|
<Series.Sequence durationInFrames={DURATIONS.promise}>
|
|
<PromiseSeq />
|
|
</Series.Sequence>
|
|
{rest.map((clip) => (
|
|
<Series.Sequence key={clip.id} durationInFrames={clip.durationFrames}>
|
|
<ClipStage clip={clip} />
|
|
</Series.Sequence>
|
|
))}
|
|
</Series>
|
|
</AbsoluteFill>
|
|
);
|
|
};
|