나레이션 데모
에이전트 하나, 오후 하나, VP가 핸드폰에서 볼 수 있는 .mov 하나. 검증된 레시피(5개 클립, 각 69–188초):
- 인터뷰 — 사용자에게 다섯 가지 질문(아래 참조). 이미 확보한 컨텍스트에서 스토리를 초안 작성합니다. 사용자는 직접 쓰는 것이 아니라 편집합니다.
- 장면 명세 작성 (
clips/<name>.yaml): 4–8개 장면, 각 장면당 약 25–45단어 나레이션(≈ 10–18초 분량). 모든 장면은 페이오프(payoff) — 그 순간에 등장하는 것 — 가 있으며, 비디오는 페이오프가 마지막이 아닌 초반에 보이도록 편집됩니다. - 렌더링:
scripts/render_clip.py clips/<name>.yaml. 장면별로: TTS → 캡처 → 비디오를 오디오에 맞춤 → 자막 → 믹싱; 이후 연결(concat). - 검토 — 렌더러가 작성한 스틸 이미지(
out/_<clip>_<scene>.png)를read도구로 확인한 후 완료 처리합니다. 단일 장면만 다시 렌더링하려면--only s2를 사용합니다. - 게시 (선택 사항):
scripts/slack_post.py <channel_id> out/<clip>.mov "<blurb>".
모든 과정은 재실행 가능하며 캐싱됩니다(TTS는 텍스트 해시 기준, 비디오는 장면별). 따라서 한 문장을 반복 수정하는 데 몇 초밖에 걸리지 않습니다.
1. 인터뷰 (다음 질문을 한 후 초안 작성)
다섯 질문을 한 메시지에 모두 물어보세요. 부분 답변을 수락하고 나머지는 컨텍스트에서 채우되, 추정한 부분은 표시하세요.
- 누구를 위한 것이며, 얼마나 시간이 있나요? (동료 엔지니어 3분 · 임원 90초 · Slack 스크롤 60초) → 장면 수와 페이스를 결정합니다.
- 시청 후 꼭 믿어야 할 한 가지는 무엇인가요? → 장면 1의 첫 문장이자 마지막 장면의 마지막 문장이 됩니다.
- 화면에 무엇이 보여야 하나요? (UI, 터미널, 문서, 차트, 파일) → 캡처 유형을 결정합니다. URL / 명령어 / 경로를 물어보세요.
- '라이브' 순간은 무엇인가요? 시각적으로 일어나는 일(쿼리 반환, 빌드 완료, 숫자 등장). 클립당 최소 하나.
- 음성과 톤은 어떻게 할까요? 기본값:
onyx, "차분하고 명확하며 자신감 있는 기술 데모 나레이션, 중간 속도, 숫자에 약간 강조". 더 가벼운 톤을 원하면alloy/nova를 제안하세요.
그런 다음 스토리보드를 표로 제안하세요(장면 · 화면 내용 · 한 줄 나레이션 요지 · 페이오프). 렌더링 전에 승인을 받으세요. 5장면 클립 렌더링은 약 3–6분이 소요됩니다. 승인 전에는 렌더링하지 마세요.
2. 장면 명세
clip: 1-setup # 출력 이름 → out/1-setup.mov
size: 1440x900 # 선택 사항: 모든 장면의 공통 프레임 (맞춤 + 패딩) — 화면과 브라우저 장면을 혼합할 때 설정
voice: onyx
instructions: "차분하고 명확하며 자신감 있는 기술 데모 나레이션. 중간 속도, 마침표에서 자연스러운 멈춤. 숫자에 약간 강조."
scenes:
- id: s1 # 짧고 안정적인 ID → 캐시 키
caption: "주제 · 하나의 그래프, 네 가지 증거" # 하단 자막; 60자 이내 유지
narration: "…25–45단어…" # TTS가 숫자를 망가뜨리면 숫자를 단어로 표기("만 팔천")
# 장면당 정확히 하나의 캡처 소스:
url: "http://127.0.0.1:4300/#mode=purchase" # (a) Playwright를 통한 브라우저 — 추가 액션 가능:
actions: # 로드 후 순서대로 실행; `ready`가 페이오프를 표시
- fill: ["#prompt", "Acne Studios"]
- press: ["#prompt", "Enter"]
- wait: ".layer-chip" # 선택자(45초 타임아웃) → ready 설정
- click: "button:has-text('fit')"
- select: ["#graph-layout", "brand"]
- scroll_to: "#join" # scrollIntoView(긴 문서용) → ready 설정
- hover: ".card"
# video: "cache/cr_s3" # (b) 사전 녹화된 디렉토리(page.mov + marks.json) 또는 파일 — record_screen.sh 참조
# still: "docs/figure.png" # (c) 정적 이미지(차트, 슬라이드) — Ken-Burns 효과 없이 나레이션 동안 유지
hold: 2 # 마지막 액션 후 추가 초
speed: 1 # 소스 사전 속도 조절(8 = 느린 크롤링을 8배속으로 표시)
scale: 2560 # 장면별 다운스케일 너비(화면 녹화); font: 28 = 이 장면의 자막 픽셀 크기
좋은 클립을 만든 규칙:
- 페이오프 우선. 렌더러는 각 장면을
ready보다 약 2.5초 전에 시작하고, 나레이션이 더 길면 마지막 프레임을 패딩하고, 더 짧으면 트리밍합니다. 45초 녹화에서 40초째에 페이오프가 있는 장면은 페이오프로 시작하는 12초 장면이 됩니다. - 나레이션과 화면이 일치해야 함. 나레이션에 나오는 모든 대문자 이름과 모든 숫자는 화면에 보이거나 곧 보여야 합니다. 그렇지 않은 문장은 삭제하세요.
- 장면당 하나의 아이디어. 문장이 "또한"으로 시작한다면, 새로운 장면이거나 삭제 대상입니다.
- 자막은 개요입니다. 자막만 읽어도 전체 스토리를 알 수 있어야 합니다.
- 나레이션에 숫자를 조작하지 마세요. 결과 파일이나 방금 생성한 라이브 응답에서 가져오고, 반올림한 경우 "약"이라고 말하세요.
3. 캡처 소스
- 브라우저 (
url+actions) — Playwright Chromium, 1440×900, 헤드리스, webm 녹화.@playwright/test와 Chromium이 설치된 Node 프로젝트 필요;PW_REPO=/path/to/that/project설정(기본값: 스킬 자체의pw/—scripts/setup.sh를 한 번 실행하여 생성). - 화면 / 터미널 (
video) —scripts/record_screen.sh <scene> <seconds> [--rect x,y,w,h] -- <command…>:screencapture -V를 시작하고, 명령어를 실행하며, 명령어가MARK를 출력하여 페이오프를 알리면marks.json에started(ms)를 기록합니다. 주의:screencapture -V는 픽셀이 변경될 때만 진행됩니다. 정적 터미널 창은 녹화를 멈춥니다. 1Hz 클록 라인 같은 무언가를 계속 움직이거나,show_file.sh(파일을 클록과 함께 페이지 단위로 표시)를 사용하세요. 넓은 캡처는DEMO_SCALE=2560으로 다운스케일하세요. 개인정보: 사각형(--rect, 포인트 단위;screencapture -R)을 녹화하여 의도한 창만 캡처 — 창 제목, 탭 바, 사이드바, 독, 알림 또는 배경 창은 제외됩니다. 게시 전에 모든 리뷰 스틸을 읽고 다음을 확인하세요: 화면에 고용주, 호스트, 내부 URL, 다른 사람을 식별하는 내용이 있습니까? 페이지로 표시하는 텍스트(show_file.sh)는 중립화하세요 — 이는 실제 파일이 아닌 데모입니다. - 정적 이미지 (
still) — PNG/JPG를 나레이션 동안 유지; 차트, 테이블, 슬라이드에 사용. - 사전 녹화 (
video: file.mov) — 장면에ready: <seconds>와 함께 사용.
4. 렌더링, 검토, 게시
export OPENAI_API_KEY=… # TTS (gpt-4o-mini-tts); 또는 호환 프록시용 TTS_URL=https://<proxy>/v1/audio/speech TTS_API_KEY=…
scripts/render_clip.py clips/1-setup.yaml # 전체 클립 → out/1-setup.mov (+ out/_1-setup_<scene>.png 스틸)
scripts/render_clip.py clips/1-setup.yaml --only s2 # 한 장면 재렌더링 후 재연결
DEMO_FONT=26 scripts/render_clip.py … # 자막 크기(1440px용 22, 2560px 소스용 26–30)
검토: read로 out/_*.png 스틸 2~3개를 확인합니다. 브라우저 장면에서 wait failed: … Timeout 로그가 나타나면 앱이 느리거나 선택자가 변경된 것입니다. 장면은 t=0부터 렌더링됩니다(페이오프 컷 없음) — 선택자를 수정하거나 앱이 유휴 상태일 때 해당 장면을 --only <id>로 다시 실행하세요. (각 장면의 시작 프레임) — 자막이 읽기 쉬운지, 페이오프가 화면에 보이는지, 민감한 정보(토큰, 바이어 수준 행, 다른 사람의 메시지)가 보이지 않는지 확인하세요. 가능하면 .mov의 처음과 마지막 5초를 시청하세요.
Slack에 3줄 이내의 블러브(내용 · 길이 · 숫자 하나)와 함께 게시합니다. scripts/slack_post.py C0123… out/1-setup.mov "…"는 ~/.config/slack-mcp/credentials.json에 있는 slack-mcp 브라우저 세션 자격 증명을 사용합니다. 파일이 없으면 사용자에게 파일과 블러브를 전달하세요.
5. 규칙 (프로젝트 정리 유지)
clips/ 명세 · cache/ TTS + 장면별 비디오(gitignore됨) · out/ 최종본 + 스틸 · blurbs/ Slack 텍스트. 클립 이름은 재생 순서로 지정(0-…, 1-…, 2a-…). 인터뷰 표가 포함된 storyboard.md를 유지하세요. 이는 비디오 이후 사람들이 요청하는 산출물입니다.
의존성: ffmpeg/ffprobe, node ≥ 18, Playwright Chromium(브라우저 장면용), macOS screencapture(화면 장면용), TTS 키.
scripts/setup.sh가 가능한 항목을 확인하고 설치합니다.