프로모션 비디오 제작 (로컬 도구 체인)
에이전트 독립적: 파일을 읽고 쉘 명령을 실행할 수 있는 모든 AI 코딩 에이전트를 위해 작성되었습니다. install.sh를 통해 Claude Code, GitHub Copilot CLI / Microsoft Scout, Google Antigravity에서 기본 지원되며, Cursor, Codex, Windsurf 등에서는 일반 마크다운 런북으로 사용할 수 있습니다.
이 도구 체인은 Idan Shimon(Microsoft, github.com/idanshimon)에 의해 처음 선보였습니다. 그의 58초짜리 개념 설명 비디오(로컬에서 AI 에이전트가 이 스택을 사용하여 $0 비용으로 제작)가 이 스킬의 모태가 되었습니다.
설치 위치를 가정하지 마십시오: SKILL.md가 포함된 디렉토리로 PROMO_VIDEO_SKILL_DIR을 설정하십시오. 디렉토리 내 scripts/에는 TTS 스크립트, 약어 발음 도우미, 고정된 TTS 의존성 파일이 있습니다. assets/에는 구성 스켈레톤, 공간 구성 요소 템플릿, 결정 로그 스키마가 있습니다. references/에는 머신당 한 번 실행하는 도구 체인 부트스트랩(install.md)이 있습니다.
핵심 원칙: 스크립트가 먼저 확정됩니다. 모든 다운스트림 숫자는 측정된 내레이션 오디오로부터 도출되며, 절대 추측하지 않습니다.
설명형이 아닌, 프로모션: 이 스킬은 WHY를 판매합니다. 대상, 문제, 변화, 증거, 하나의 행동 촉구입니다. 작동 방식('how it works')은 최대 하나의 비트만 허용되며, 사용자가 지시할 때만 사용합니다. 작동 방식을 가르쳐 달라는 요청은 동반되는 explainer-video 스킬의 몫입니다.
도구 프로필
| 도구 | 위치 | 검증된 버전 | 라이선스 | 역할 | 호출 방식 |
|---|---|---|---|---|---|
| OpenMontage | ~/OpenMontage | 2026-07 체크아웃 | AGPLv3 (도구 전용; 비디오에 포함되지 않음) | 파이프라인 거버넌스: 단계 순서, 결정 로그, 게이트, 렌더 후 셀프 리뷰 규율 | AGENT_GUIDE.md + pipeline_defs/animation.yaml을 읽을 것; 당신(에이전트)이 오케스트레이터이며, 데몬은 실행되지 않음 |
| Kokoro | PyPI | Kokoro-82M (hexgrad), kokoro==0.9.4 | 코드 Apache-2.0; 모델 가중치 Apache-2.0 | 내레이션 TTS, 24kHz 모노 WAV (씬별) | Python 3.12 venv; KPipeline(lang_code="a"), 음성 af_heart, speed 파라미터 |
| HyperFrames | ~/hyperframes | 0.7.57, 소스에서 빌드 | Apache 2.0 | HTML/CSS/GSAP 구성 → 헤드리스 Chrome을 통한 결정적 MP4 | node ~/hyperframes/packages/cli/dist/cli.js <cmd> |
| FFmpeg | ~/FFmpeg 소스; 빌드 디렉토리의 바이너리 | git master (N-125608 시대) | GPL 빌드 (libx264 활성화) | 음악 합성(aevalsrc), 인코딩, ffprobe QA, 음량, 플랫폼 패키징 | hyperframes 명령어보다 먼저 빌드 디렉토리를 PATH에 추가 |
| bun | npm-global | 1.3.14 | — | hyperframes 모노레포 빌드 | npm i -g bun (brew cask가 한 번 실패한 적 있음) |
| Node.js | nvm | 24.x (≥22 필요) | — | hyperframes CLI 실행 | — |
| Python | uv 관리 | 3.12 | — | Kokoro 추론 | 항상 venv (uv venv --python 3.12 .venv); black + pylint 검증 통과 |
| espeak-ng | Homebrew 또는 apt | any | GPL | Kokoro G2P 폴백 (필수) | macOS: brew install espeak-ng; Debian/Ubuntu: sudo apt install espeak-ng |
| gsap | vendored 파일 | 3.x gsap.min.js | GSAP 표준 라이선스 | 구성 내 타임라인 애니메이션 | video/assets/에 복사 — 절대 CDN <script src> 사용 금지 (오프라인 결정성) |
환경 확인 (머신당 한 번)
export FFMPEG_BUILD_DIR="${FFMPEG_BUILD_DIR:-$HOME/ffbuild}"
export HYPERFRAMES_DIR="${HYPERFRAMES_DIR:-$HOME/hyperframes}"
export PATH="$FFMPEG_BUILD_DIR:$PATH"
node "$HYPERFRAMES_DIR/packages/cli/dist/cli.js" doctor
FFmpeg, FFprobe, Node, Chrome 모두 프로덕션 전에 통과해야 합니다 (다른 doctor 행은 이 스킬이 사용하지 않는 선택적 구성 요소입니다). 이 네 가지 중 하나라도 실패하면 $PROMO_VIDEO_SKILL_DIR/references/install.md를 따르십시오. 머신당 한 번의 전체 부트스트랩: 시스템 패키지(macOS/Linux), 소스 도구 클론(OpenMontage 참조 체크아웃 포함), libx264를 포함한 FFmpeg 소스 빌드, HyperFrames CLI 빌드, 에이전트 권한 팁입니다. doctor가 이미 통과했을 때 이 파일을 로드하지 마십시오.
Chrome: CLI는 ~/.cache/puppeteer/ 아래에 캐시된 chrome-headless-shell을 자동으로 사용합니다 (없으면 한 번 다운로드). 첫 번째 TTS 실행도 마찬가지로 Python 패키지와 Kokoro-82M 가중치를 다운로드합니다. 이 일회성 다운로드 후, 합성, 검증, 렌더링, 패키징은 오프라인 경로를 사용합니다.
프로젝트 레이아웃 (비디오당 생성, 자체 git 저장소, 원자적 커밋)
<project>/
.venv/ # uv venv, python 3.12
.gitignore # .venv/ __pycache__/ video/snapshots/ .DS_Store
README.md # 제작 방법 + 재현 방법 (마지막에 작성)
production/
research/research-brief.md # 출처 포함 사실 + 정직한 경쟁사 표
proposal.md # 컨셉, 씬 목록, 전달 약속, 런타임 결정
script/script.md # 확정된 내레이션
scene_plan/scene-plan.md # 씬별 시각 자료 + 모션 노트
checkpoints/decision-log.json # 추가 전용; (category, subject)가 키
checkpoints/self-review.md # 렌더 후 증거
assets/audio/ # kokoro WAV + durations.json
renders/ # 마스터 + 플랫폼 파생 파일
youtube/ # metadata.md + thumbnail.jpg
tools/tts_generate.py # 이 스킬에서 복사, SCENE_NARRATIONS 편집
tools/tts_pronounce.py # scripts/에서 복사, 약어 발음 (편집 금지)
tools/pronunciation.local.json # 선택적 프로젝트 사전 오버레이
video/
index.html # 구성 파일
assets/gsap.min.js # vendored
assets/audio/ # WAV 복사본 (구성 기준 상대 경로)
1단계 — 리서치 (오프라인 우선)
주제의 자체 저장소/README/문서를 조사하여 결과와 증거(숫자, 도입률, 전후 결과)를 찾고, 아키텍처는 찾지 마십시오. research-brief.md를 생성하십시오:
- 포지셔닝 블록 (필수 — 스크립트는 이것 없이 확정될 수 없습니다). 각각 한 문장씩: 대상(누구를 위한 것인지), 문제(오늘날 그들이 느끼는 어려움), 변화(그들에게 어떤 변화가 일어나는지), 차별화 요소(이것을 선택해야 하는 하나의 이유). 이것이 메시지이며, 모든 후속 단계는 이 메시지를 따릅니다.
- 제품 사실, 각각 출처에 대해 검증됨 — 혜택을 뒷받침하는 경우에만 유지합니다('그래서?' 테스트: 시청자가 얻는 것은 무엇인가?).
- 경쟁사 표(약점 → 주제가 이를 어떻게 해결하는지). 정직성 규칙: 경쟁사의 실제 장점은 인정하십시오(경쟁사 열에 ✓ 표시는 당신의 신뢰도를 높여줍니다).
- "스크립트에 사용 가능한 주장" 목록.
2단계 — 제안 + 결정
proposal.md를 작성하십시오: 포지셔닝 블록 재진술, 5~7 씬 컨셉, 목표 ≈60초, 전달 약속('모션 주도형, 슬라이드쇼 아님'), 도구 체인 표. decision-log.json에 기록하십시오 — $PROMO_VIDEO_SKILL_DIR/assets/decision-log.json의 빈 스키마에서 시작하십시오(필드: category, subject, selected, options_considered, rejected_because, confidence): pipeline_selection, render_runtime_selection (고려된 대안으로 Remotion 기록 — OpenMontage 하드 규칙), composition_mode (주요 작업에는 atelier), voice_selection, music_selection, output_profile, approval_gates, 그리고 cta_selection — 정확히 하나의 행동 촉구(저장소에 별표 / 설치 / 데모 보기 / 사이트 방문); 프로모션은 기능 목록으로 끝나면 실패합니다. 사용자의 브리핑이 선택을 고정했다면, approved_by: "user brief"를 표시하십시오. 나중에 결정을 변경했습니까? 동일한 (category, subject)로 새 항목을 추가하고 이전 항목은 절대 편집하지 마십시오.
3단계 — 스크립트 (확정 지점)
~150–155 단어 ≈ 60초 (kokoro af_heart 속도 1.1 기준). 프로모션 아크 (WHY 판매): stakes/pain 훅 → 공개(이름 + 약속하는 변화) → 시청자 세계에서의 혜택 → 증거(정직한 비교, 실제 결과) → 열망 → 결정 로그의 단일 CTA. 기능은 혜택에 대한 증거로만 나타납니다: 모든 문장은 '그래서?' 테스트(시청자가 얻는 것은 무엇인가?)를 통과해야 하며, 그렇지 않으면 다시 작성해야 합니다. 최대 하나의 how-it-works 비트만 허용되며, 사용자가 지시할 때만 사용합니다 — 메커니즘을 가르치는 것은 explainer-video 스킬의 역할입니다. 어조: 2인칭, 현재 시제, 짧고 강력한 문장("당신은 더 빠르게 배송합니다", "더 빠른 배송을 가능하게 합니다"가 아님). 약어는 스크립트와 화면 텍스트 모두에서 자연스럽게 작성하십시오("OID", "MCP", "AI"). 합성 시 tools/tts_pronounce.py는 자체 사전에서 구어체 형태(단어 vs 글자별)를 도출하며, 알 수 없는 약어는 글자별로 철자되고 보고됩니다. 선택적 tools/pronunciation.local.json에서 도메인 용어를 분류하십시오({"NASA": "word", "ECG": "letters", "NGINX": "engine X"}). 모두 대문자 강조는 피하십시오: 모두 대문자 단어는 약어로 처리됩니다. 씬당 단어 예산 ≈ 60초 중 해당 씬의 비중 × 2.55 단어/초.
4단계 — 내레이션 + 타이밍 도출 (핵심 트릭)
제공된 고정된 직접 의존성을 사용하여 프로젝트의 Python 3.12 환경을 만들고, 일반 템플릿을 복사한 후 SCENE_NARRATIONS만 편집하십시오:
uv venv --clear --python 3.12 .venv
uv pip install --python .venv/bin/python \
-r "$PROMO_VIDEO_SKILL_DIR/scripts/tts-requirements.txt"
mkdir -p tools
cp "$PROMO_VIDEO_SKILL_DIR/scripts/tts_generate.py" tools/tts_generate.py
cp "$PROMO_VIDEO_SKILL_DIR/scripts/tts_pronounce.py" tools/tts_pronounce.py
PYTORCH_ENABLE_MPS_FALLBACK=1 .venv/bin/python tools/tts_generate.py
mkdir -p video/assets
curl -fL https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js \
-o video/assets/gsap.min.js
첫 번째 합성은 Kokoro-82M 가중치(~330MB)를 다운로드합니다. 이후 실행은 캐시를 사용하며 오프라인으로 가능합니다. 씬별 WAV + durations.json을 작성합니다. 실행에서 알 수 없는 약어 WARNING이 출력되면, 해당 토큰을 tools/pronunciation.local.json에 분류하고 타이밍을 도출하기 전에 다시 실행하십시오.
150–155 단어, 속도 1.1을 시작 추정치로 취급하고 보정값으로 사용하지 마십시오. 총 시간이 62초를 초과합니까? 속도를 올리거나 단어를 줄인 후 다시 측정하십시오.
타임라인 도출 (씬 사이 패드 ≈0.3–0.5초):
n_start[0] = 0.5 # 도입부
scene_start[i] = n_start[i] - 0.15..0.4 # 시각 자료가 VO 바로 직전에 도착
n_start[i+1] = n_start[i] + dur[i] + 0.3..0.35
TOTAL = 마지막 n_end + 0.6..0.9 꼬리 (페이드 투 블랙)
이 숫자들은 일치해야 하는 세 곳에 입력됩니다: 씬 data-start/data-duration, <audio data-start> 값, JS const S1..Sn 타임라인 상수. WAV를 video/assets/audio/에 복사하십시오.
5단계 — 배경 음악 (다운로드 없음)
소스 빌드된 FFmpeg로 결정적 합성 패드를 생성하십시오. TOTAL_SECONDS를 도출된 구성 총 시간으로 설정하십시오. 외부 샘플이나 개인 참조는 필요하지 않습니다:
TOTAL_SECONDS=60.5
FADE_SECONDS=3.5
FFMPEG_BIN="${FFMPEG_BUILD_DIR:-$HOME/ffbuild}/ffmpeg"
FADE_OUT_START="$(awk -v total="$TOTAL_SECONDS" -v fade="$FADE_SECONDS" \
'BEGIN { printf "%.3f", total - fade }')"
mkdir -p production/assets/audio
"$FFMPEG_BIN" -hide_banner -y \
-f lavfi \
-i "aevalsrc=0.07*(sin(2*PI*110*t)+0.65*sin(2*PI*110.7*t)+0.45*sin(2*PI*164.81*t)+0.30*sin(2*PI*220.9*t)):s=48000:d=${TOTAL_SECONDS}" \
-af "highpass=f=55,lowpass=f=1200,afade=t=in:st=0:d=2,afade=t=out:st=${FADE_OUT_START}:d=${FADE_SECONDS},pan=stereo|c0=c0|c1=c0" \
-c:a pcm_s16le production/assets/audio/music-bed.wav
희박하게 유지하십시오. data-volume="0.12..0.16"로 믹스하면 분위기로 읽힙니다. 프로모션 에너지를 위해 이 범위의 위쪽에 두되, 내레이션보다 높지 않게 하십시오. WAV를 video/assets/audio/에 복사하고 별도의 오디오 트랙을 사용하십시오.
6단계 — 구성 작성 (HyperFrames 계약)
$PROMO_VIDEO_SKILL_DIR/assets/composition-skeleton.html에서 시작하십시오(아래 모든 규칙을 구현합니다). HTML을 작성하기 전에 $HYPERFRAMES_DIR/skills/hyperframes-core/SKILL.md를 훑어보십시오.
구조: 독립형 index.html, 루트 <div data-composition-id="X" data-start="0" data-duration="TOTAL" data-width="1920" data-height="1080">, 씬은 <section class="clip" data-start data-duration data-track-index="1">, window.__timelines["X"]에 동기적으로 등록된 하나의 gsap.timeline({paused:true}), 트윈 위치 = S<i> + local_offset.
많은 디버깅 시간을 소모하는 규칙들 — 미리 준수하십시오:
- 모든
<video>/<audio>는 명시적이고 비어 있지 않은id를 가져야 합니다. HyperFrames에서, id 없는 오디오는 브라우저 미리보기에서 재생될 수 있지만 SILENT로 렌더링됩니다(lint가 오류를 표시함; 무시하지 마십시오). - GSAP 트윈 요소의 CSS에 초기
transform:을 절대 넣지 마십시오. 대신tl.fromTo(el, {y:24, opacity:0}, {y:0, opacity:1, ...})를 사용하십시오; GSAP가 전체 transform을 소유합니다. CSS에서opacity: 0만 있는 것은.to()전용 공개에 괜찮습니다. - 배경 채움은 전체 블리드 CHILD(
position:absolute; inset:0)에 넣고, 루트에는 절대 넣지 마십시오(루트 배경은 검은색으로 렌더링될 수 있습니다). - 미디어 요소는 루트의 직접 자식이며, 별도의 오디오 트랙에
data-track-index를 사용합니다(예: 7=VO, 8=음악). - 무한 반복 금지(
repeat: -1금지 — 유한 반복 사용, 예: 커서 깜빡임repeat: 9, yoyo: true),Date.now/Math.random금지, 네트워크 가져오기 금지, CDN 스크립트 금지. - 모든 곳에 고유
id; transforms/opacity만 애니메이션(+ SVG 선 그리기를 위한strokeDashoffset). - 중간 씬에 나타나는 요소: 숨겨진 초기 상태 +
S<i>+offset에서 트윈.fromTo는immediateRender를 가지므로, 트윈 전 검색은 from 상태를 표시합니다 — 이것이 검색 안전을 보장합니다. :rootCSS 변수의 디자인 토큰 = 테마; ground/ink/accent + 타입 스택을 교체하여 리스킨합니다. 렌더러가 번들로 제공하는 글꼴(JetBrains Mono)을 사용하십시오 — 시스템 글꼴은 어차피 대체됩니다(lint info가 매핑을 알려줍니다).- 보조 텍스트는 ground 색상에 대해 WCAG AA 이상을 유지하십시오 —
check는 대비에 실패하고 사용할 수 있는 접근성 색상을 출력합니다. - 의도적인 오버랩/스크롤 언더: 발견 사항을 설명하지 않고
data-layout-allow-overflow/-overlap/-occlusion으로 표시하십시오.
씬 모티브 조언: OUTCOME 상태를 보여주십시오 — 시청자의 세계가 개선된 모습(전후 대비, 사라진 문제, 화면에 나타난 결과). 주제의 실제 UI/도메인(가짜 검색 팔레트, 터미널, 트리, 표)에서 뒷받침하는 모습을 혜택에 대한 증거로 가져오되, 메커니즘 투어는 피하십시오. 내러티브 아크는 템플릿을 따르지만, 모티브는 그래서는 안 됩니다. $PROMO_VIDEO_SKILL_DIR/assets/composition-skeleton.html 및 $PROMO_VIDEO_SKILL_DIR/assets/spatial-component-templates/ 아래에 있는 모티브 예제를 참조하십시오.
7단계 — 사전 렌더 검증
cd video
node "$HYPERFRAMES_DIR/packages/cli/dist/cli.js" check
모든 수정 사항을 적용하고 다시 확인하십시오. check는 lint 규칙을 실행하고 구성이 올바른지 확인합니다. 구성이 유효할 때까지 렌더링하지 마십시오.
8단계 — 렌더링
cd video
node "$HYPERFRAMES_DIR/packages/cli/dist/cli.js" render
마스터 MP4가 생성됩니다. production/renders/에 복사하십시오.
9단계 — 포스트 프로덕션 + 셀프 리뷰
checkpoints/self-review.md를 채우기 위해 OpenMontage AGENT_GUIDE.md에 있는 셀프 리뷰 규율을 따르십시오. FFprobe를 사용하여 품질, 음량, 지속 시간을 확인하십시오. 모든 것이 통과하면 YouTube 메타데이터와 썸네일을 준비하십시오.
10단계 — 최종 커밋 + README
모든 파일을 커밋하고 README.md에 제작 방법과 재현 방법을 기록하십시오. 이 프로세스는 완전히 재현 가능해야 하며, 모든 에이전트가 동일한 입력과 도구 체인을 사용하여 동일한 비디오를 생성할 수 있어야 합니다.