Reel — 스토리 편집(전체 화면, 움직임)
스피커는 항상 전체 화면으로 유지됩니다. 스토리는 크리에이터 본인의 클립으로의 컷어웨이, 음성에 따라 나타나는 스티커/카드/위젯, 반응하는 마스코트, 그리고 스피커 뒤에 큰 글자나 브랜드 배경이 위치하는 깊이 효과(人物 매트)로 전달됩니다. 작업 예시: examples/story/story_stoodi_inviteraw.py.
인수: $ARGUMENTS
기본값(사용자가 명시적으로 변경을 요청한 경우만 변경)
먼저 reel.config.json을 읽습니다(없으면 reel.config.example.json으로 대체).
| 항목 | 기본값 |
|---|---|
| 자막 | 없음. 키워드 그래픽은 괜찮지만, 실행 중인 대본은 절대 표시하지 않음 |
| 오디오 | 테이크 자체 믹스, 그대로 사용 — finish.py가 리먹싱. 효과음, 배경 음악 없음 |
| 테이크 | 이미 컷, 그레이딩, 음악이 추가되었다고 가정 → 컷이나 그레이딩하지 않음. Raw/미편집 → 먼저 질문 |
| B-롤 | 설정 파일의 broll_dir; 설명은 work/_catalog/<폴더>/NOTES.md에 있음 |
| 브랜드 | 설정 파일의 brand + palette. 마스코트 파일이 없으면 마스코트 장면 없음 |
| CTA | 댓글 키워드를 화면에 크게 표시; 팔로우 카드 = @<creator.handle> 텍스트만, 얼굴 크롭 절대 안 함 |
| 언어 | 설정 파일의 language(Whisper 및 사용할 텍스트 클래스를 결정) |
입력
테이크 이름과 주제는 인수에서 받아옵니다. 한 번에 하나의 메시지로, 누락된 항목(테이크가 이미 컷/그레이딩/음악이 포함되었는지, 댓글 키워드, Whisper가 잘못 들을 수 있는 이름이나 전문 용어, 포함/제외할 클립)만 질문합니다.
파이프라인
PY = .venv/Scripts/python.exe -X utf8 (Windows) or .venv/bin/python -X utf8 (macOS/Linux). 긴 단계는 백그라운드에서 실행.
| # | 단계 | 명령 / 동작 | 확인 |
|---|---|---|---|
| 1 | 보기 | ffprobe로 테이크 정보 확인; 연락처 시트 ffmpeg -i raw/<take> -vf fps=1/5,scale=216:384,tile=9x2 -frames:v 1 work/<take>/sheet.jpg 생성 및 확인 | 머리 위 여유 공간, 텍스트가 프레임 내에서 반전되어 있는지?, 이미 그레이딩되었는지? |
| 2 | 전사 | $PY tools/transcribe.py <take> --prompt "<names, jargon>" | 이름 교정; 단어 간격 > 3초 → 해당 구간 다시 전사 |
| 3 | 얼굴 | $PY tools/face_track.py <take> | FACE = (x, y) 및 머리 위쪽 출력 |
| 4 | B-롤 | $PY tools/story_assets.py <take> catalog; NOTES.md 읽기; 아직 설명되지 않은 클립 조각을 보고 NOTES.md에 해당 행 추가 | HDR / 반전 / VFR 플래그 확인 |
| 5 | 비트 시트 | work/<take>/beats.md 작성: time | words | mode | what. 아래 규칙 참조, 기법은 references/techniques.md 참조 | 모든 비트가 단어에 연결되어야 함 |
| 6 | 매트 | 깊이 효과가 있으면: $PY tools/story_assets.py <take> matte (~80초 분량 CPU 약 8분, 백그라운드, ffmpeg 병렬 처리 안 함) | work/<take>/matte/fg.webm |
| 7 | 테이크 모듈 | examples/story/story_stoodi_inviteraw.py 복사 → tools/story_<slug>.py (slug: 테이크 이름, 영숫자가 아닌 문자는 _로 변환); NAME, FACE, BROLL, TILES, MIRRORED, scenes, SCENES 설정(DUR는 선택 사항 — 자동 탐지). 마스코트 이름 = assets/mascot 내 파일명(소문자, 공백은 -로 변환) | |
| 8 | 자산 | $PY tools/story_assets.py <take> all (마스코트, 로고, B-롤 트림, 타일) | |
| 9 | 빌드 | $PY tools/story_build.py <take>; work/<take>/sc 디렉토리에서: npx hyperframes lint public → 0 오류 (nested_structure 경고는 정상); npx hyperframes snapshot public -o ../snaps --no-end --describe false --at <모든 비트>; 연락처 시트 읽고, 수정, 다시 빌드 | |
| 10 | 빠른 렌더링 | $PY tools/finish.py <take> --seconds 12; work/<take>/sc/quick_sheet.jpg 확인 | 오버레이가 들어오고 나가야 함; 색상이 어둡지 않아야 함 |
| 11 | 전체 렌더링 | $PY tools/finish.py <take> (~80초 분량 약 12분, 백그라운드) → final/<take>/, 확인, qa/sheet.jpg 생성 | 시트를 프레임별로 확인 |
| 12 | 보고서 | 경로, 길이, 비트, 사용된 클립, 반전/톤 매핑된 항목, 결과 확인. 새로 알게 된 내용이 있으면 이 스킬에 추가 |
비트 규칙
- 2~4초마다 무언가가 변경되어야 함. 스피커만 나오는 구간은 약 5초를 초과하지 않음.
- 모드를 혼합: 스피커 + 스티커 · B-롤 컷어웨이(런타임의 약 25–30%) · 깊이 효과(릴당 4
6회) · 스피커가 페이스캠 버블로 표시되는 전체 화면 그래픽(최대 12회). - 큐(Cue) =
transcript.json에서 음성이 시작되는 시간. 각 요소는 다음 비트 전에 사라져야 함. - 깊이 단어는 머리 위에 위치(3단계에서 얻은 머리 위쪽): 한 번에 하나의 큰 단어, 이전 단어는 날아가 사라짐. 여러 줄이 쌓이면 머리 뒤에 가려지므로 주의.
- 안전 구역: y 150(Instagram 헤더) 위나 y 1560(캡션) 아래에는 텍스트를 배치하지 않음; y 1100~1650 사이에서는 x < 960 유지(좋아요/댓글 버튼 영역).
- 숫자로 표시되는 사실은 반드시 테이크에서 가져와야 함. 설명적 레이블은 괜찮음.
- 페르시아어/아랍어 텍스트:
.fa클래스 사용(요소에만 RTL 적용 —<html>에dir="rtl"사용 금지, 검은색으로 렌더링됨),letter-spacing: 0, 숫자는fa()사용, 큰 0은 라틴어0으로 표시(페르시아어 0은 점). 라틴 텍스트:.en; 큰 표시 단어:.blk.
엔진 계약 — 이미 해결된 버그
- Scene =
def scene(c: Comp):c.clip(layer, id, start, end, html, track),c.broll(name, a, b, mstart),c.matte(n, a, b),c.j(js...). 레이어:under(버블 뒤),tiles,behind(테이크와 매트 사이),fw,broll,over,fx. - 클립 div는 id
k-<id>를 가짐;#<id>-i또는 내부 id를 애니메이션 — 절대.clip자체를 애니메이션하지 않음. - 화면 밖에서 슬라이드 인하는 모든 요소는 from 상태에
autoAlpha:0이 있어야 함. 그렇지 않으면 일찍 보임. - 요소의 첫 번째 트윈은 초기 상태를 설정함(
fromTo는 즉시 렌더링됨); 이후 트윈:.to또는immediateRender:false사용. from:"random"지연 또는Math.random사용 금지 — DOM 순서는 Python에서random.Random(seed)로 셔플.- 같은 속성, 같은 요소: 한 트윈이 끝나야 다음 트윈이 시작됨(lint
overlapping_gsap_tweens확인). #camscale/x/y/clipPath/borderRadius가 펀치인, 버블, 모자이크를 제어. 매트는#cam내부에 있으므로 깊이 효과는 카메라 스케일에 관계없이 정렬 유지.- 읽을 수 있는 텍스트가 포함된 전면 카메라 B-롤 →
MIRRORED에 추가. 메인 테이크는 절대 반전하지 않음. - HLG/HDR 소스는
story_assets.py에 의해 톤 매핑됨.finish.py가 "HDR auto-promotion"에서 멈추면, 해당 경로를 벗어난 소스가 있음. hyperframes snapshot은 렌더링의 증거가 아님 — 10단계가 그 역할을 함.