Communitygithub.com

learnermaker/project-demo-video-skill

소프트웨어 프로젝트, 해커톤 출품작 또는 제품을 위한 내레이션과 자막이 포함된 1080p 데모 비디오(3분 이하)를 코드로 제작합니다. Playwright와 ffmpeg-static을 사용하여 실제 앱을 녹화하고, 실제 터미널 출력 결과를 재생하며, 실제 소스 코드를 둘러보고, 다이어그램을 그리고, 음성(기본 Windows 내장 음성, ElevenLabs 선택 가능)을 추가하고 자막을 새깁니다. 데모, 피치 또는 제출 비디오를 만들거나, 다시 만들거나, 시간을 조정해야 하거나, 프로젝트가 실제로 작동한다는 것을 비디오로 증명해야 할 때 사용하세요.

project-demo-video-skill란 무엇인가요?

이 스킬은 소프트웨어 프로젝트의 데모 비디오를 **코드로** 제작하여 변경 사항이 발생한 후 몇 분 만에 다시 렌더링할 수 있도록 합니다. 화면에 보이는 모든 것은 실제 앱, 실제 명령 출력, 실제 소스 파일로서 어떤 것도 모형(mock-up)이 아닙니다. Playwright(Chromium screencast)를 사용하여 앱 상호 작용을 녹화하고, ffmpeg-static을 사용하여 최종 비디오를 인코딩하며, 1080p 해상도, 30fps, H.264 코덱을 출력합니다. 또한 내레이션 음성(Windows TTS 기본, macOS `say` 또는 Linux `espeak-ng`로 대체 가능, ElevenLabs 지원)을 생성하고 동기화된 ASS 자막을 비디오에 새깁니다. 프레임워크에 구애받지 않으며 Claude Code, Cursor, Codex 등 AI 코딩 에이전트와 함께 사용할 수 있도록 설계되었습니다. 주요 워크플로우는 10단계(요구사항 매핑, 작업 공간 설정, 드라이 런 및 버그 수정, 스토리보드, 템플릿 적용, 증명 캡처, 오디오 생성, 녹화/조립, QA, 결과물 생성)로 구성되며, 각 단계는 완료 조건을 충족해야 다음 단계로 넘어갑니다. 중요한 규칙으로는 사용자 리포지토리에 직접 쓰지 않음(작업 사본 사용), 화면의 모든 내용을 정직하게 표시(시뮬레이션된 항목은 라벨링), 비밀 정보 미포함, AI 도구 언급 금지, 대회 규칙 준수, 무료 티어 유지 등이 있습니다.

지원 대상✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/learnermaker/project-demo-video-skill/tree/HEAD/skills/project-demo-video

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

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

문서

project-demo-video

데모 비디오를 코드로 제작하여 변경 사항이 발생한 후 몇 분 만에 다시 렌더링할 수 있도록 합니다. 화면에 보이는 모든 것은 실제 앱, 실제 명령 출력, 실제 소스 파일입니다. 어떤 것도 모형(mock-up)이 아닙니다.

이 스킬은 덜 숙련된 코딩 에이전트도 단계별로 따라 할 수 있도록 작성되었습니다. 순서대로 단계를 따르세요. 각 단계에는 완료 조건이 있습니다. 조건이 충족될 때까지 다음 단계로 넘어가지 마세요. 파일을 다시 작성하는 대신 templates/에서 복사하세요.

설치: 이 전체 폴더(SKILL.md 및 templates/)를 에이전트의 스킬 폴더(예: ~/.claude/skills/project-demo-video/)에 복사하거나, 에이전트가 SKILL.md를 가리키도록 하면 됩니다.

결과물

출력물설명
<name>-voiced.mp41920x1080, 30fps, H.264 + AAC, 자막 내장, 음성 포함
<name>-captions-only.mp4동일한 화면, 무음 오디오 트랙(사용자가 이 옵션을 선택하거나 자신의 음성을 녹음할 수 있음)
<name>.srt파일 형태의 자막, 내레이션과 동일한 텍스트
thumbnail.png1280x720
narration.md, shotlist.md실제 타임스탬프가 포함된 대본; 각 장면과 그 출처
frames/실제로 확인한 QA 프레임들
repo/변경되거나 새로운 프로젝트 파일, 리포지토리 경로 그대로 유지
COPY_LIST.md, README-snippet.md, REPORT.md복사할 위치, "작동 확인" README 섹션, 완료된 작업 및 위험 사항

0. 절대 위반해서는 안 되는 규칙

  1. 소스는 읽기 전용. 사용자의 실제 리포지토리에 쓰거나, 커밋하거나, 푸시하지 마세요. 복사본에서 작업하고(robocopy/cp, node_modules, .git, 빌드 출력물 제외), 그곳에서 npm ci를 실행하세요. 유지할 가치가 있는 변경 사항은 리포지토리 경로를 미러링하여 출력 폴더 아래에 보관하고 COPY_LIST.md를 함께 작성하세요. git 작업은 다른 도구나 사람이 수행합니다.
  2. 화면에서 정직하세요. 시뮬레이션된 모든 것(장치, 커넥터, 결제, 모델)은 비디오에서 시뮬레이션되었다고 표시되어야 합니다. 존재하지 않는 하드웨어, 배포 또는 사용자를 암시하지 마세요. 프로젝트 자체의 "실제 vs 시뮬레이션" 표를 복사하세요. README가 잘못된 경우 보고서에 그렇게 명시하고 해당 주장을 반복하지 마세요.
  3. 화면이나 전달 파일에 비밀 정보를 포함하지 마세요: 토큰, API 키, .env 내용, 개인 키, 실제 이메일. 프로젝트에서 문서화한 개발 토큰(예: Bearer dev-alice)은 괜찮습니다. 코드를 보여줄 때는 비밀을 제외한 라인 범위를 선택하세요.
  4. 코드를 작성한 AI 도구나 해당 공급업체를 프레임, 내레이션, 자막 또는 전달 문서에서 언급하지 마세요(사용자가 달리 요청하지 않는 한). 제품 구성의 일부인 런타임 모델 ID는 모델 이름으로 표시될 수 있습니다. 완료하기 전에 grep -ri로 확인하세요.
  5. 대회 규칙을 준수하세요. 길이 제한(보통 "3:00 미만"; 2:50을 목표로 함)과 필수 기술 규칙을 읽으세요. 비디오는 필수 기술이 실제로 실행되는 모습을 보여줘야 하며, 단순히 언급만 해서는 안 됩니다.
  6. 무료 티어 유지, 배포 금지, 구매 금지. 배포하거나, 클라우드 리소스를 생성하거나, 비용을 지출하지 마세요. 단계에 자격 증명이나 사용자의 녹화물이 필요한 경우 한 번 명확하게 요청하고 다른 모든 작업을 계속 진행하세요.
  7. 사용자에게만 자격 증명, 사용자 자신의 녹화물 또는 실제로 사용자에게 달린 선택권(예: 음성)에 대해 물어보세요. 그 외의 모든 것은 스스로 결정하고 결정한 사항을 말하세요.

1. 사전 요구사항 (확인하고 추정하지 마세요)

node --version          # 22+ (24 권장; .mjs 파일은 일반 JavaScript여야 하며 TypeScript 캐스트 금지)
  • 프로젝트 복사본에서: @playwright/test(Chromium 설치 포함)와 ffmpeg-static이 있어야 합니다. 확인: node -e "import('@playwright/test').then(async m=>{const b=await m.chromium.launch();console.log(b.version());await b.close()})" 및 node -e "import('ffmpeg-static').then(m=>console.log(m.default))". 없고 프로젝트에서 새 종속성을 금지하는 경우 별도의 도구 폴더에 넣고 해당 폴더를 가리키도록 하세요.
  • 음성: System.Speech(내장)가 포함된 Windows PowerShell. macOS에서는 say, Linux에서는 espeak-ng를 사용하세요. 그런 다음 templates/lib/tts.mjs를 조정해야 합니다(synthAll 및 tts.ps1만 OS별로 다름).
  • ffmpeg-static에는 ffprobe가 함께 제공되지 않습니다. WAV 헤더(templates/lib/tts.mjs의 wavSeconds)와 레코더 자체 타이밍을 사용하여 지속 시간을 측정하세요.
  • Windows 셸 참고 사항: 백슬래시, 정규식 또는 $가 포함된 파일에는 Write 및 Edit 도구를 선호하세요. 인라인 python -c/heredoc을 사용한 JS 재작성은 이스케이프를 자동으로 손상시킵니다. 포그라운드 sleep은 일부 에이전트에서 차단됩니다. until grep -q "done" log; do sleep 10; done으로 대기하세요.

2. 단계

단계 A: 요구사항 및 증명 매핑

규칙 페이지, README, 프로젝트 자체 문서 및 스토리라인 노트를 읽으세요. 노트에 표를 작성하세요:

심사위원이 확신해야 하는 사항비디오에서 이를 증명하는 방법장면
필수 기술이 코드에서 호출됨실제 호출 확인 가능: 요청 로그, 터미널, 코드 발췌...
경험이 작동함브라우저에서 실행 중인 앱...
실제와 시뮬레이션 구분프로젝트 자체 표...

완료 조건 모든 행에 장면이 할당되었습니다.

단계 B: 작업 공간

  1. 리포지토리를 임시 폴더에 복사한 후 설치: npm ci.
  2. templates/를 복사본에 video/로 복사합니다(구조 유지: video/lib, video/pages, video/scenes, video/final.mjs, video/narration.mjs, ...). package.json 스크립트에 "video:final": "node video/final.mjs"를 추가합니다. 종속성은 추가하지 마세요.
  3. 복사본의 .gitignore에 video/out/을 추가합니다.

단계 C: 실제 앱 드라이 런 및 버그 찾기 (녹화 전에 수행)

심사위원이 할 것처럼 앱을 시작하고(npm run demo 또는 README 명령어), 문서화된 기본 포트에서 실행되도록 하여 화면에 표시되는 모든 명령이 그대로 사용되도록 합니다. 작은 Playwright 스크립트로 앱을 구동하고, 스토리의 각 단계 후에 1920x1080 스크린샷을 찍어 확인합니다. 확인 사항:

  • 로그인, 빈 패널, 빨간색 오류 배너, 콘솔 오류;
  • 스토리에 필요한 모든 상태가 실제로 렌더링되는지(참조 프로젝트의 첫 번째 실행은 로그인 실패, 빈 받은 편지함 및 누락된 주요 보기를 보여주었으며, 모두 이전 초안 비디오에 숨겨져 있었음);
  • 텍스트 중복 또는 잘림.

실제 버그는 복사본에서 최소한으로 수정하고, 프로젝트 자체 검사(npm run verify, 테스트, 타입체크)를 실행하고 각 수정 사항을 COPY_LIST.md에 기록합니다. 앱에 와이어 트래픽을 표시할 방법이 없으면 가짜를 만드는 대신 작은 선택적 훅(요청 로그)을 추가하세요. 완료 조건 전체 스토리가 두 번 연속으로 깔끔하게 실행되고 프로젝트 검사가 통과했습니다.

단계 D: 스토리보드 및 내레이션

  1. 장면을 선택합니다. 참조 스토리(약 2:50):
장면길이보여주는 내용
훅12초하나의 변경 사항이 여러 커밋에 영향을 미치는 모습(애니메이션 카드)
부트6초모든 것을 시작하는 하나의 명령어, 실제 출력
시뮬레이션(앱)60초8-10개 비트; 2개 비트에서 요청 로그 열림
증명 터미널17-30초동일한 서버에 대한 공식 클라이언트 또는 CLI; 인증 확인
코드 투어16초필수 기술을 증명하는 3-4개의 실제 발췌문
아키텍처, 클라우드 스택, 실제 vs 시뮬레이션30초다이어그램, 스택 소스, 정직성 표, 오픈소스 카드
마무리12초태그라인, 리포지토리 링크, 빌드 크레딧, "X는 시뮬레이션됨" 바닥글
  1. video/narration.mjs 작성: scenes = [{ id, title, segs: [{ id, text }] }]. 모든 seg는 하나의 음성 라인이자 하나의 자막입니다(text가 그대로 표시됨). 규칙: 초당 약 2.4단어; 총 음성 단어 수 330-380; 라인당 하나의 아이디어; 화면 카드에는 약 12단어 이하; 문장 케이스; 과장 금지.
  2. 음성 엔진 전용 say 테이블에 발음 힌트를 추가합니다(두문자어는 철자, "Alexa+"는 "Alexa plus"로); 자막 텍스트는 절대 변경하지 마세요. 완료 조건 내레이션이 낯선 사람도 이해할 수 있는 스토리로 읽히고 증명 맵의 모든 행이 음성으로 언급되거나 화면에 표시됩니다.

단계 E: 템플릿 적용

프로젝트별로 다음만 편집합니다:

파일편집 내용
video/narration.mjs장면 및 라인
video/scenes/sim.mjs앱 단계: 입력하거나 클릭할 내용, 선택자, 카메라 줌(lens(...), zoomWire)
video/scenes/static.mjs터미널 명령어(CMD), codeBlocks(파일 + 첫 번째 줄의 정규식 + 줄 수 + 강조 정규식), 훅/마무리 페이지 호출
video/proofs.mjs, video/proof-auth.mjs주장을 증명하는 실제 명령어; 상태 코드와 오류 이름만 출력
video/pages/*.html텍스트, 색상, 타일, 다이어그램 상자, 표 행, 링크
video/lib/overlay.mjs앱 레이아웃에 대한 SIM_CSS 선택자(레이아웃 규칙 참조), 티커의 데이터 소스
video/final.mjsLIMIT 상수 및 장면 순서(순서는 narration.mjs에서 가져옴)
video/thumbnail.mjs, video/docs.mjs제목 및 SHOT 표

고장나지 않은 한 lib/recorder.mjs, lib/director.mjs, lib/tts.*는 편집하지 마세요.

단계 F: 실제 증명 출력 캡처

node video/proofs.mjs는 새 서버를 시작하고, 실제 명령어(공식 인스펙터 또는 클라이언트 CLI, 인증 확인, 설치/부트 출력)를 실행하고, 출력된 내용을 video/out/proofs.json에 저장합니다. 터미널 장면은 이 캡처된 출력을 HTML 터미널에서 읽을 수 있는 크기로 재생합니다. 규칙: 실제 명령어 텍스트를 표시합니다(패키지 경로만 단축); 매우 긴 출력은 눈에 띄게 자릅니다(어두운 "... N lines ..." 줄); 칩에 "Recorded run"을 넣습니다; 토큰을 절대 출력하지 마세요; JSON을 예쁘게 출력하고 그렇게 했다고 말하세요.

단계 G: 내레이션 오디오

기본값(계정 불필요): node video/final.mjs --rate 2는 내장 Windows 음성(Microsoft Zira)으로 라인당 하나의 WAV를 만들고 각 길이를 측정합니다. Rate 1은 참조 스크립트에 대해 약 150초의 음성, rate 2는 약 139초, rate 3은 너무 빠릅니다. 전체 비디오를 제한 시간 미만으로 유지하는 가장 느린 속도를 선택하세요. 선택적 사람 같은 음성: 부록 A를 참조하세요. 아래의 모든 것은 어느 쪽이든 동일하게 작동합니다.

단계 H: 녹화 및 조립

node video/final.mjs --rate 2                   # 모든 것: tts, proofs, 모든 장면 녹화, 두 컷 모두 조립
node video/final.mjs --rate 2 --reuse-proofs    # proofs 재실행 건너뛰기
node video/final.mjs --scenes sim,close --reuse-tts --reuse-proofs   # 일부 장면만 재녹화
node video/final.mjs --assemble-only            # 재녹화 없이 리먹싱(자막, 오디오)만
node video/final.mjs --tts-dir video/out/tts-human --name my_video --single    # 다른 오디오 폴더에서 빌드

전체 실행은 6-10분이 소요됩니다. 백그라운드에서 실행하고 로그를 폴링하세요. 스크립트는 총 시간이 180초 이상이면 실패합니다. 170초를 목표로 하세요. 중요: 내레이션 텍스트나 음성을 변경하면 지속 시간이 변경되므로 모든 장면을 다시 녹화해야 합니다(마크는 지속 시간에서 가져옴). 한 장면의 비주얼만 변경하는 경우 --scenes <id> --reuse-tts를 사용하세요.

단계 I: QA (절대 건너뛰지 말고, 로그를 절대 신뢰하지 마세요)

  1. 최종 MP4에서 각 내레이션 라인의 끝에서 프레임을 추출합니다: ffmpeg -ss <markEnd-0.3> -i final.mp4 -frames:v 1 frame.png (타임라인은 video/out/timeline.json에 있음). 확인합니다. 1080p에서 가독성, 빈 공간, 잘린 텍스트, 자막 위치 및 크기, 동기화(자막 이벤트가 화면에 표시됨), 비밀 정보 없음, 공급업체 언급 없음, 정직성 라벨 존재.
  2. ffmpeg -i final.mp4 -af volumedetect -f null -: 평균 약 -22 dB, 최대 0 dB 미만.
  3. 두 컷 모두 지속 시간이 제한 미만; 두 파일 모두 비디오 및 오디오 스트림이 있음.
  4. grep -ril "<vendor words>" video/pages video/narration.mjs <output docs>는 허용된 모델 ID를 제외하고 아무것도 반환하지 않음.
  5. 프로젝트 검사를 다시 실행합니다(npm run verify; UI가 변경된 경우 e2e).
  6. 모두 통과할 때까지 수정하고 다시 렌더링합니다.

단계 J: 결과물

node video/docs.mjs <outdir>로 narration.md 및 shotlist.md를 생성합니다(실제 타임스탬프). MP4, SRT, 썸네일(node video/thumbnail.mjs <png>), QA 프레임 및 변경된 리포지토리 파일(원본과 복사본 간의 모든 차이 파일, 리포지토리 경로 유지)을 복사합니다. COPY_LIST.md(버그 수정 먼저, 그 다음 새 파일, 제안된 커밋 메시지, "복사 후 빌드 및 확인 실행"), README-snippet.md(실행 명령어, 진입점 파일:라인, 클라이언트 파일:라인, 스킬 파일, 비디오 링크 플레이스홀더) 및 REPORT.md(완료, 사용자가 해야 할 일, 위험)를 작성합니다. 확인할 수 없었던 사항을 명확히 말합니다(예: 음성을 들을 수 없음).

3. 파이프라인 작동 방식 (변경하기 전에 읽어보세요)

레코더 (lib/recorder.mjs). Playwright 자체 recordVideo는 비트 전송률을 제한하고 작은 텍스트를 흐리게 하므로, 이 스크립트는 Chromium screencast를 사용합니다. JPEG 프레임(퀄리티 92)이 도착할 때 스탬프가 찍히고 디스크에 기록되며 libx264 crf 17로 30fps 상수로 인코딩됩니다. 정적 화면은 적은 프레임을 생성합니다. 마지막 프레임이 유지됩니다. 타임스탬프는 하나의 클록(performance.timeOrigin + performance.now())에서 가져오며, 디렉터도 동일한 클록을 사용합니다.

디렉터 (lib/director.mjs). 각 내레이션 라인은 세그먼트입니다. d.seg(id, actions, {tail})는 시작 시간을 기록하고, 화면 동작을 실행한 다음, 라인이 tail * 0.45초 더 재생될 때까지 기다립니다(꼬리는 제한 시간에 맞게 전체적으로 잘립니다). 마크(id, 시작, 종료, 텍스트)는 video/out/<scene>.marks.json에 저장됩니다. 오디오는 나중에 기록된 마크에 배치되므로 그림과 음성이 어긋날 수 없습니다. 비트 규칙: 세그먼트 내에서 동작을 수행하고 동작이 내레이션보다 먼저 끝나는지 확인하세요. 라인이 동작보다 짧으면 두 개의 라인으로 분할합니다(이것이 하나의 스토리 비트가 sim9와 sim10이 된 이유입니다).

오디오 (final.mjs). 각 WAV는 adelay로 전역 시작 시간만큼 지연되고, amix=normalize=0으로 믹스되며, alimiter로 제한되고, apad=whole_dur=<total>로 패딩됩니다(패딩이 없으면 -shortest가 비디오 끝을 자릅니다).

자막. 큐는 내레이션 텍스트를 문장 끝(예: "2.0"과 같은 소수는 보호됨)과 82자를 초과하는 쉼표에서 분할하며, 시간은 길이에 비례하여 분할됩니다. .srt가 제공됩니다. 내장된 버전은 PlayResX: 1920, PlayResY: 1080 및 글꼴 크기 36이 포함된 생성된 ASS 파일을 사용합니다. force_style로 SRT를 내장하지 마세요. libass는 288픽셀 높이 캔버스에 대해 크기를 조정하여 텍스트가 약 4배 정도 너무 커집니다.

오버레이 (lib/overlay.mjs), 녹화된 앱 페이지에만 주입됩니다(앱 자체는 편집되지 않음...).

4. 레이아웃 규칙

비디오는 1920x1080입니다. 모든 HTML 페이지(터미널, 코드, 다이어그램, 표)는 다음 레이아웃을 따라야 합니다:

  • 콘텐츠 영역: 왼쪽 1800px(중앙 1440px + 오른쪽 패딩). 전체 너비 1920px에서 왼쪽 여백 60px + 콘텐츠 1800px + 오른쪽 여백 60px.
  • 제목 표시줄: 상단 80px, #1a1a2e 배경, 왼쪽 정렬 24px 텍스트. 제목 아래 8px 두꺼운 #e94560 밑줄(색상 #e94560, 높이 8px).
  • 콘텐츠 영역: 제목 표시줄 아래에서 시작, 하단 60px(자막 영역)까지. 터미널, 코드 블록, 다이어그램, 표는 이 영역 내에 배치되어야 합니다.
  • 자막 영역: 하단 60px, 높이 60px. 자막은 이 영역 중앙에 표시됩니다. PlayResY: 1080에서 글꼴 크기 36pts.

5. 문제 해결

문제해결 방법
스크립트가 실패: "No chromium installed"npx playwright install chromium을 실행하세요.
ffmpeg 오류: "Unknown encoder 'libx264'"ffmpeg-static이 올바르게 설치되었는지 확인하세요. node -e "import('ffmpeg-static').then(m=>console.log(m.default))")
비디오가 180초 제한을 초과함--rate를 높이거나(예: --rate 3), 내레이션 텍스트를 줄이거나, 각 장면의 tail을 줄이세요.
자막이 너무 큼SRT를 force_style로 내장하지 마세요. 대신 생성된 ASS 파일을 사용하세요.
오디오가 비디오와 동기화되지 않음모든 장면을 다시 녹화하세요: --scenes all --reuse-proofs (tts는 재사용 가능).

부록 A: 사람 같은 음성(ElevenLabs)

ElevenLabs API 키가 필요한 경우:

  1. ELEVENLABS_API_KEY 환경 변수를 설정하세요.
  2. templates/lib/tts.mjs의 synthAll 함수가 ElevenLabs API를 사용하도록 수정하세요(샘플 코드는 ElevenLabs 문서 참조).
  3. node video/final.mjs --rate 1.5로 실행하세요(ElevenLabs 음성은 일반적으로 더 느림).

참고: ElevenLabs는 유료 서비스입니다. 무료 티어를 유지해야 하는 경우(규칙 6) 기본 Windows TTS를 사용하세요.

부록 B: macOS/Linux에서 작업하기

Windows가 아닌 환경에서는:

  1. macOS: say 명령어 사용; Linux: espeak-ng 설치(sudo apt-get install espeak-ng).
  2. templates/lib/tts.mjs에서 OS별 함수를 조정하세요(synthAll 및 tts.ps1).
  3. video/final.mjs에서 플랫폼별 경로를 조정해야 할 수도 있습니다.

부록 C: 사용자 정의 썸네일

썸네일은 node video/thumbnail.mjs <png>로 생성됩니다. video/thumbnail.mjs를 편집하여 제목, 색상 및 레이아웃을 변경하세요. 썸네일은 1280x720이어야 하며 비디오의 주요 주제를 명확하게 나타내야 합니다.

관련 스킬