Communitygithub.com

나레이션 A/B 비디오

PR이나 기능에 대한 A/B 검증 비디오를 제작합니다: Playwright로 각 측의 브라우저 실행을 녹화하고(타이밍 마크, OTP 코드 같은 동적 입력을 위한 훅 포함), 제목/증거 카드와 A/B 코너 배너를 렌더링하며, macOS say로 각 세그먼트를 음성으로 설명하고, 모델 대기 시간을 가속하며, 마지막 프레임을 고정해 음성 길이에 맞춘 후 하나의 mp4로 연결합니다. 트리거: 나레이션 비디오 생성, 나레이션 데모 비디오, A/B 비디오 녹화, 비디오로 증명, 팀용 데모 비디오, 음성 해설 화면 녹화.

B 비디오란 무엇인가요?

이 스킬은 macOS Apple Silicon 환경에서 PR이나 기능의 A/B 테스트 결과를 나레이션과 함께 하나의 mp4 비디오로 자동 제작합니다. Playwright로 각 측(A/B)의 브라우저 동작을 녹화하고, 제목/증거/판정 카드와 A/B 배너를 투명 PNG로 생성한 후 ffmpeg로 합성합니다. macOS `say` 명령어로 각 세그먼트에 음성을 입히며, ElevenLabs API도 지원합니다. 모델 응답 대기 시간은 최대 10배속으로 압축하고, 음성이 끝날 때까지 마지막 프레임을 유지하여 음성-영상 동기화를 맞춥니다. Playwright의 헤드리스 셸 누락 문제를 우회하고, Homebrew ffmpeg의 `drawtext` 필터 의존성을 없애기 위해 카드를 투명 PNG로 처리합니다. `hooks.ts` 파일을 통해 OTP 코드나 세션 ID와 같은 동적 입력을 주입할 수 있으며, JSON 기반 시나리오/카드/계획 파일로 완전히 설정 가능합니다. 단일 측 데모(비 A/B)도 지원하며, 결과를 먼저 검증한 후 녹화하도록 장려합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sblattj/agent-chandlery/tree/main/skills/narrated-ab-video

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

B 비디오은(는) 무엇을 하나요?

추가 트리거

PR 비디오 둘러보기, 데모 녹화 만들기, 음성 해설 데모, 두 스택 녹화, 설명 비디오, drawtext 필터 없음, Playwright recordVideo, 헤드리스 셸 없음

주장 집합당 하나의 mp4: 제목 카드, A 클립, B 클립, 증거 카드, 판정 카드, 각각 음성 처리됨.

Apple Silicon을 탑재한 macOS용으로 제작됨 (say, ~/Library/Caches/ms-playwright 아래 Playwright 캐시, chrome-mac-arm64). 다른 환경에서는 CHROMIUM_EXE를 설정하고 --eleven을 사용하세요. Bun, ffmpeg 및 Python 3이 필요합니다.

실행 방법

S=${SKILL_DIR:-~/.claude/skills/narrated-ab-video}
$S/preflight.sh /tmp/vid                                  # chromium, playwright ffmpeg, ffmpeg, say 음성
bun $S/record.ts a.json raw/a-run                         # 측당 하나의 시나리오 (examples/scenario.json 복사)
bun $S/record.ts b.json raw/b-run
bun $S/cards.ts cards.json cards                          # 제목/증거/판정 PNG + banner-A/B.png
python3 $S/build.py plan.json                             # [--voice Samantha] [--rate 182]
open -a "QuickTime Player" demo.mp4                       # 일시 정지 상태로 열림; 직접 재생하지 마세요

예제는 examples/에 있습니다: scenario.json (단계: wait, select, click, clickIfPresent, type, send, waitStable, expand, fetchText, waitText, probeEnabled, reload; 자세한 내용은 record.ts의 헤더 참조), cards.json, plan.json. 계획 파일의 경로는 상대 경로입니다. 실제 두 클립으로 구성된 4세그먼트, 40초 빌드에서 종단 간 검증되었습니다.

이렇게 만든 이유 (발견된 문제점)

  • Playwright의 고정 헤드리스 셸이 종종 다운로드되지 않음. record.ts와 cards.ts는 실패하는 대신 가장 최근에 캐시된 chromium-* 전체 빌드(또는 $CHROMIUM_EXE)를 실행합니다.
  • recordVideo는 Playwright 자체 ffmpeg 빌드가 필요함. 없으면 webm이 생성되지 않습니다. preflight가 browsers.json에서 리비전을 읽고 bunx playwright install ffmpeg를 실행합니다.
  • Homebrew ffmpeg에는 drawtext가 없을 수 있음. 배너는 cards.ts에서 생성된 투명 PNG이며 build.py에서 오버레이됩니다. 글꼴이나 필터를 찾을 필요가 없습니다.
  • 모델 응답 시간이 10~60초의 정적 구간을 만듦. send는 "전송됨"을 표시하고 waitStable은 "응답됨"을 표시합니다. build.py는 해당 구간을 speed(기본 10배속)로 재생하고 나머지는 1배속으로 재생합니다.
  • 나레이션이 일반적으로 클립보다 김. build.py는 음성이 끝날 때까지 마지막 프레임을 유지(tpad stop_mode=clone)하므로, 응답이 화면에 설명되는 동안 그대로 표시됩니다.
  • 동적 입력(OTP 코드, 이전 턴의 ID): type 단계에 {{fn:NAME}}을 넣고 시나리오 옆에 있는 hooks.ts에서 NAME(page, state)를 내보냅니다. state.lastJson은 captureJsonFrom과 일치하는 마지막 POST JSON을 보관합니다.
  • 선택자: 버튼은 역할과 이름 정규식("Tools used", "^Send$")으로 일치시킵니다. CSS는 입력과 선택에만 사용됩니다. 스크립팅 전에 빠른 스크린샷으로 이름을 찾으세요.
  • 연결하려면 동일한 인코딩이 필요함. 모든 세그먼트는 동일한 코덱, 비트율 및 오디오 레이아웃으로 다시 인코딩된 후 -c copy로 결합됩니다.
  • 더 나은 음성: ELEVENLABS_API_KEY=... build.py plan.json --eleven <voice_id> (모델 eleven_multilingual_v2; 음성+텍스트 해시로 클립이 캐시되므로 재실행 시 캐릭터를 소모하지 않음). 키는 0600 권한의 파일에 보관하고 절대 계획 파일에 넣지 말며, 사용 후 삭제하세요.
  • 단일 측 데모 (A/B 없음): 하나의 시나리오를 녹화하고 cards.json에 배너 하나를 제공한 후, 동일한 클립을 여러 계획 세그먼트로 나누고 각 주장에 대해 spans로 대기 시간을 단축(예: 5배속)합니다. 동일한 빌드이며 A 측이 없습니다.
  • 사용자가 <voice_id> <key>만 붙여넣을 수 있음. 이는 --eleven <voice_id>로 다시 빌드한다는 의미입니다. 느린 음성은 비디오 길이를 늘립니다(say로 72초가 92초가 됨). build.py는 음성이 끝날 때까지 마지막 프레임을 유지하기 때문입니다.
  • 촬영 전 해체된 데모 재구축: 수명이 짧은 인증(로컬 JWT, 터널 URL, 임시 클라우드 리소스)은 보통 다음 날이면 만료됩니다. 녹화 전에 한 번의 API 실행으로 재확인하세요. 401 "JWT 토큰 만료" 오류가 발생하면 결과가 도착하지 않는 턴을 촬영하게 됩니다.
  • 원본 클립 손실: 이전 출력 mp4에서 해당 측의 영상을 잘라내고(배너 코너 색상을 샘플링하여 해당 스팬을 찾음), <dir>/a.webm으로 -f mp4와 빈 marks.json과 함께 저장한 후 하나의 명시적 스팬을 사용합니다. 배너가 이미 포함되어 있으므로 banner는 생략합니다.
  • 오디오: say -o는 파일만 씁니다. 사용자의 명시적 동의 없이 비디오나 오디오를 재생하지 마세요. 일시 정지 상태로 열고 사용자에게 넘기세요.
  • 촬영 전에 증명하세요. 먼저 A/B를 실행하고 결과(API 또는 로그)를 확인하세요. 비디오는 입증된 결과를 보여줄 뿐, 테스트 자체가 아닙니다.
  • 카드는 클립 크기와 일치해야 함. build.py는 크기를 조정하지 않습니다. "viewport": [1440, 900]으로 녹화하려면 cards.json에서 "size": [1440, 900]이 필요합니다. 그렇지 않으면 연결 시 프레임 크기가 혼합됩니다.
  • 비동기 UI는 고정 대기 시간이 아닌 마크가 필요함. waitText는 정규식이 나타나면 마크를 표시하고(또는 timeout:<mark>), probeEnabled는 waitMs로 버튼을 폴링하여 marks.json에 probes[]를 기록하며, type/send/click에 tryMs는 중단 대신 skipped:<step>을 표시하므로 "비활성 상태 유지" 측도 클립을 완료할 수 있습니다.
  • 클릭할 요소와 동일한 요소를 프로브하세요. CSS 프로브(button[aria-label=...])가 역할-이름 send와 다른 일치하는 버튼을 찾아 probeEnabled가 비활성 상태를 읽는 동안 전송이 진행될 수 있습니다. probeEnabled에서는 "role"을 사용하세요.
  • 개발 모드 오버레이(쿼리 개발자 도구, 오류 토스트)가 클립에 포함됨. 첫 번째 wait 후에 {"clickIfPresent": "^Close .* devtools$"}를 추가하세요.
  • "안정화"되지 않는 응답을 기다리는 경우: 라이브 경과 시간 배지가 매초 본문 텍스트를 변경합니다. 빌드 전에 marks.json에서 replied 마크가 있는지 확인하세요.
  • 증거가 보이는 텍스트에 있는지 확인하세요. 접힌 도구 카드는 확장되지 않을 수 있습니다(expand는 조용히 아무것도 클릭하지 않음). marks.json body에서 주장 문자열을 읽으세요. 없으면 프롬프트를 변경하거나 대신 카드에서 증명하세요.
  • "기본 측 실패" 주장을 촬영하기 전에 동등성을 증명하세요. 실제 환경에서는 발생하지 않는 이유(로컬에서는 켜져 있고 원격에서는 꺼져 있는 플래그)로 로컬 기본 측이 실패하여 나레이션에서 잘못 주장하는 경우가 있었습니다. 먼저 원격의 플래그와 일치시킨 후 촬영하세요.
  • 제목 카드: 테스트된 커밋과 각 측의 플래그. 클립 세그먼트당 하나의 주장. 판정 카드로 종료.

관련 스킬

  • narrated-screenshot: 비디오 대신 주석이 추가된 단일 스틸 이미지.
  • pr-ab-evidence: 이 비디오가 촬영하는 빨간색/초록색 A/B 방법의 결과.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬