make-ai-short — 생성형 숏폼, 엔드투엔드은(는) 무엇을 하나요?
비디오 모델이 픽셀을 생성하고, Remotion은 클립과 캡션만 합성합니다.
blue-man #1 "The Door"에서 입증됨. /make-short(100% TSX)의 자매 스킬로, 동일한 규율에 TSX가 필요 없었던 세 가지 관문이 추가됨: 생성 전 비용 명시, character.json에서 캐릭터 잠금, 마지막 프레임 조건을 통한 루프 고정.
모든 명령은 저장소 루트에서 실행합니다. .env에 FAL_KEY(클립 + 이미지)와 ELEVENLABS_API_KEY(음성)가 필요합니다.
세 가지 철칙
- 절대 텍스트로 캐릭터를 두 번 생성하지 마십시오.
ai-shorts/<char>/character.json+character.png가 잠긴 참조입니다. 캐릭터가 필요한 모든 정지 이미지는gen_image.py --ref character.png로 생성하고, 모든 클립은 이미지를 동영상으로 변환하며 이 캐릭터를 포함하는 프레임에서 시작합니다. 캐릭터 시트가 아직 없으면 먼저 하나를 만들고(사용자 승인 필요), 그 이후에 다른 작업을 진행합니다. - 비용을 지출하기 전에 반드시 명시하십시오. 추정치가 아닌 계산값: Seedance 1080p 오디오 오프 =
h×w×24×dur/1024토큰 × $1.2/1M ≈ $0.0583/s (fal 가격 API 2026-07-14 검증 — 전체 표 및 Wan/Veo-Lite 함정은ai-shorts/IDEAS.md참조). 항상generate_audio:false— 음성은 ElevenLabs, 효과음은 자체 SFX 파이프라인 사용. - 운이 아닌 제약 조건으로 루프를 만듭니다. 샷 1의 진짜 프레임 0(프롬프트 이미지가 아닌 클립에서 추출)이 루프 대상입니다. 마지막 샷은 이 프레임을
end_image_url로 전달하고(Seedance/Kling), 컴포지션은 마지막 ~10프레임 동안 (remotion/src/shots/ai-1/Ai1Door.tsx의LoopSettle패턴) 정확히 그 정지 이미지로 안착합니다.
아티팩트 계약
ai-shorts/<name>/
script.md · beats.json — /make-short와 동일한 계약 (vo[]에 실제 단어 시간 포함)
character.json — 잠긴 참조 (+ video_model 블록 = 선택된 모델)
shots/NN-slug.png — 각 샷의 시작 프레임 (gen_image.py --ref character.png [--ref scene])
shots/NN-slug.json — gen_clip 사이드카 (모델, 페이로드, 요청 ID)
shots/upload-urls.json — fal 저장소 URL (재생성 시 재사용 가능)
voice/ · sfx-plan.json · output/ [voice/output gitignored]
remotion/src/shots/ai-N/ — 컴포지션 + vo.gen.ts
media/projects/<name>/ — staticFile()용 클립 복사본 (커밋됨 — 픽셀은 재현 불가능;
shots/*.mp4 작업 복사본은 gitignored)
작업 순서 (비디오보다 음성 먼저)
- 스크립트 — script.md + beats.json. 후크는 프레임 0에서 구성됨; 논제는 루프를 원하게 해야 함 (blue-man #1: "도착은 신화다" → 결말 = 같은 문을 향해 걷기). ~80-100단어, 2.5-2.7 wps 창. 절대 참여 유도 아웃트로 없음 — 보상에서 끝나며, 루프가 결말.
- 음성 먼저 —
python tools/gen_voice.py --beats ... --emit-ts remotion/src/shots/ai-N/vo.gen.ts(.env의 음성 및 모델 사용, 또는 시리즈에 특정 해설자가 필요할 경우 beats.json의"voice"블록 사용 (예: GeorgeJBFqnCBsd6RMkjVDRZzb+ eleven_v3 태그); LINE CHECK 플래그에 따라 make-short Stage 4와 동일하게 처리, 클립 생성 전에 실행). 실제 단어 시간이 샷 창을 결정하며, Seedance는 4-12초 정수 기간을 지원하므로 샷 길이는 창보다 반올림하여 올림. 대화(둘 이상의 캐릭터): beats.json"cast"가 각 화자를 해당character.json에 매핑(해당"voice"블록 사용), 각 음성 라인은"who"에서 화자 이름을 지정하며,--pack 0.3이 예상 창에 맞추는 대신 실제 길이에 따라 라인을 끝에서 끝까지 배치합니다. 각 캐릭터의 음성은 해당 character.json에만 저장되며 다른 곳에는 없습니다. 대화 시 입 움직임: 비디오 모델은 누가 말하는지 모릅니다; 듣는 사람이 말하게 하고, 화자를 얼려서 웃음을 다음 문장에 걸쳐 늘립니다. 클립 후python tools/mouth_sync.py ai-shorts/<name>실행 (beats.json"shots"는 각 클립에 전역 시작 시간과 길이를 제공하며, 각 character.json"mouth"블록은 측면, 반경, 영역, 스타일 및keep_above를 제공). 모델이 만든 입을 지우고 음성 트랙의 음량에 따라 각 캐릭터의 입을 그립니다. QA도 동일한 방식: 반초마다 말하는 사람과 두 얼굴을 확인합니다. 한 라인 내에서 동작이 바뀌면(웃다가 말하기) 두 개의 클립으로 나누고, 두 번째 클립은 첫 번째 클립의 컷 지점 프레임에서 시작합니다. 그런 다음 각 입 움직임 클립을 프레임별로 스캔: 클립 중간값의 4배를 초과하는 스파이크는 컷, 빠른 제스처 또는 고스트 특징을 가진 두 프레임 모프, 해당 샷의"hold"에 모프 프레임을 나열하고 재실행하여 점프를 깔끔한 컷으로 만듭니다. - 시작 프레임 — 각 샷당 하나,
python tools/gen_image.py --model pro --aspect 9:16 --ref character.png(+ 샷-1의 장면 스틸을 두 번째 참조로, 위치/소품이 여러 샷에서 일치해야 할 때). 모든 비디오 호출 전에 모든 PNG를 QA — 여기서 표류를 싸게 수정할 수 있습니다.- 손, 의류 세부 사항, 비율 확인 — 재생성 시 프롬프트에 명시적으로 이름을 지정.
- 지오메트리는 걸을 수 있어야 함 (샷-6 교훈, 2회 재생성 비용 발생): 마지막 프레임 핀 사용 시 시작 프레임은 동작이 전진하여 최종 상태에 도달할 수 있어야 함 — 최종 프레임의 캐릭터가 시작 프레임보다 카메라에 더 가까우면 모델이 복제본을 생성함. 시작 캐릭터를 최종 상태보다 더 크고 가깝게 배치하여 전진 동작이 축소되도록 함.
- 클립 — 총 파생 비용을 명시한 후, 각 샷을 병렬로
python tools/gen_clip.py실행 (--set image_url=<fal storage url> --set 'duration="N"' --set resolution=1080p --set generate_audio=false, 마지막 샷--set end_image_url=<frame0 url>).bakeoff_clip.upload_ref로 참조를 한 번 업로드하고, shots/upload-urls.json에 URL을 보관. 위의 fal 형식은 이 체크아웃의 기본값 (CLIP_PROVIDER=fal,FAL_VIDEO_MODELin .env). kie.ai 사용 시 (--provider kie): 필드 이름은 kie 모델을 따르며 fal이 아님.bytedance/seedance-2의 경우:--aspect none --set first_frame_url=@ai-shorts/<name>/shots/NN-slug.png --set duration=N --set resolution=1080p --set generate_audio=false, 마지막 샷--set last_frame_url=@<frame0.png>.@path는 로컬 파일을 업로드 (kie는 24시간 보관하므로 upload-urls.json 재사용은 하루 동안만 유효). 비용: kie.ai/pricing, fal 표가 아님; 실행 시 사용된 크레딧을 출력.bakeoff_clip.py는 여전히 fal 전용. 모든 클립 프롬프트: 정확한 캐릭터 문구 + "샷 전체에 장면에 정확히 한 명의 캐릭터" (복제는 #1 실패 요인) + 카메라 이동 + "모프 없음" + 스타일 라인. - 클립 QA, 프레임별 — 각 클립의 시작/중간/끝 스트립 + 마지막 클립의 마지막 프레임과 루프 대상의 나란히 비교. 실패한 클립은 재생성 (재생성 비용 명시); 5초 1080p 재생성은 약 $0.29 — 복제본을 배송하는 것보다 저렴.
- 컴포지션 — 하나의 타임라인에
OffthreadVideo시퀀스, ~8프레임 크로스페이드 오버랩 (클립은 창보다 약 1초 길게 — 여유), 루트에Captions+ProgressBar, 꼬리에LoopSettle디졸브. 최종 클립을media/projects/<name>/에 복사.npm run gen, frames.mjs에서 경계와 주요 장면에서 PNG 확인, 그런 다음 render-all --scale=1 실행 후 voice.wav 먹싱. - SFX — /suggest-sfx 플로우. Gappy-VO 보정 (blue-man #1): VO 간격의 큐는 마스킹이나 덕 없음 — 조밀한 내레이션 테이블보다 약 7-8 dB 낮게 설정 (간격 우시 -9..-13, -3..-5 아님). volumedetect RMS를 음성 평균 참조와 비교하여 확인; 히어로 레이어는 음성 레벨과 한 번 닿을 수 있음. 오디션 믹스는 사용자의 청취를 기다림 (하드 게이트).
- 저장 — used_in, beats.json 실제 타이밍(gen_voice가 기록), 사이드카 커밋, 최종 보고서에 비용 기록.
완료 조건 =
모든 클립의 모든 프레임에서 캐릭터 일관성 (프레임별 QA 완료) · 루프 확인 (마지막 프레임과 프레임 0 나란히 비교) · 실제 단어 시간 기반 자막 · 각 지출 전 비용 명시 및 총계 후 보고 · SFX 오디션 렌더링 완료, 사용자의 청취 대기 · CTA 아웃트로 없음 · tools/check_text.py가 음성 및 화면 텍스트 모두에서 깨끗함 · tools/export_phone.py로 전화기용 복사본 제작 · LESSONS.md에 날짜가 포함된 항목 추가 (교훈이 작동 방식을 변경하는 경우 스킬도 일반적인 용어로 업데이트).