YouTube Shorts 자동 생성 스킬
롱폼 영상 하나에서 쇼츠 여러 개를 만든다. 스크립트는 도구이고, 판단은 Claude가 한다. 규칙 스코어러는 감정 키워드 개수를 셀 뿐 내용을 이해하지 못하므로, 후보를 추리는 데까지만 쓴다. 어떤 구간이 "쇼츠가 되는지", 제목·후크·자막을 어떻게 쓸지는 Claude가 내용을 읽고 결정한다. 이 큐레이션 단계(Phase 2b)와 검수 단계(Phase 3.5)가 품질을 좌우하므로 건너뛰지 않는다.
전체 흐름
| Phase | 주체 | 내용 |
|---|---|---|
| 0 | 스크립트 | 환경 설정 (ffmpeg, yt-dlp, 한글 폰트) |
| 1 | 스크립트 | YouTube 자막 추출 → transcript.json/srt/txt |
| 2a | 스크립트 | 후보 구간 ~25개 스코어링 → candidates.json |
| 2b | Claude | 후보 검토 → 선별 + 제목/후크 + 자막 재구성 → highlights.json |
| 3 | 스크립트 | 구간 다운로드 → 세로 변환 + 후크 오버레이 + 자막 합성 |
| 3.5 | Claude | 프레임 추출해 눈으로 검수, 문제 시 수정 후 재실행 |
| 4 | 스크립트 | 결과 보고 (metadata.json) |
실행 명령 (큐레이션 모드 — 기본)
# Phase 0~2a: 환경 설정 + 자막 추출 + 후보 생성
python3 scripts/run_pipeline.py --url "$URL" --candidates-only
# Phase 2b: Claude가 output/candidates.json → output/highlights.json 작성 (아래 가이드)
# Phase 3: 쇼츠 생성
python3 scripts/generate_shorts.py \
--highlights output/highlights.json --url "$URL" \
--output output/shorts/ --srt output/transcript.srt --layout fit_blur
# Phase 3.5: Claude가 프레임 검수 (아래 체크리스트)
명령 시간 제한이 있는 환경 (코워크 등) — 반드시 나눠 실행
코워크처럼 **명령 하나당 실행 시간 제한(~45초)**이 있는 환경에서는 위 Phase 3 한 방 실행이 타임아웃으로 죽는다 (쇼츠 1개 = 다운로드 ~20초 + 인코딩 ~25초, 여러 개면 확실히 초과). 백그라운드 실행도 명령이 끝나면 정리되므로 소용없다. 대신 다운로드와 인코딩을 명령 단위로 분리한다:
# 3-a) 구간 다운로드만 (개당 ~20초, 전부 output/cache/에 저장)
python3 scripts/generate_shorts.py --highlights output/highlights.json --url "$URL" \
--output output/shorts/ --download-only
# 3-b) 쇼츠 하나씩 인코딩 (캐시 사용 → 개당 ~25초)
python3 scripts/generate_shorts.py --highlights output/highlights.json --url "$URL" \
--output output/shorts/ --srt output/transcript.srt --layout fit_blur --only 1
# ... --only 2, --only 3 식으로 반복
- 재실행 안전: 이미 완성된 쇼츠는 자동으로 건너뛰고, 캐시된 구간은 다시 받지 않는다.
중간에 죽어도 같은 명령을 다시 실행하면 이어서 진행된다. 다시 만들려면
--force --only로 만든 결과는 기존 metadata.json에 병합된다- 전부 끝나고 검수까지 통과하면
output/cache/를 삭제해 디스크를 정리한다
Phase 2b: 하이라이트 큐레이션 (Claude가 직접 수행 — 품질의 핵심)
output/candidates.json(후보 목록)과 output/transcript_timestamped.txt(전체 타임스탬프 대본)를
읽고 최종 output/highlights.json을 작성한다. 요청받은 개수만큼 선별한다 (기본 10개, 시험용이면 2~3개).
선별 기준 — score(규칙 점수) 순이 아니라 내용을 읽고 아래를 만족하는 구간을 고른다:
- 독립성: 앞뒤 맥락 없이 그 구간만 봐도 이해된다
- 후크: 첫 1~2문장이 궁금증을 만든다 (질문, 반전, 강한 주장, 구체적 숫자)
- 완결성: 구간 안에서 작은 결론/페이오프가 나온다
- 다양성: 선택된 쇼츠끼리 주제가 겹치지 않는다
경계 조정: 후보의 start/end는 참고값이다. transcript_timestamped.txt에서 해당 구간 전후를 보고 문장이 시작되는 지점에서 시작해 결론 문장이 끝나는 지점에서 끝나도록 초 단위로 조정한다 (15~60초 권장).
제목/후크 작성:
- 자동자막의 음성인식 오류를 문맥으로 교정한다 (예: "7발1 2입니다" → 실제 발화를 문맥으로 추정)
title: 내용 요약형, 28자 이내hook: 본편 위에 크게 얹히는 문구 — 20자 이내로 짧고 강하게. 낚시가 아니라 호기심 유발
자막 재구성(subtitles): 자동자막의 롤링 파편을 문장 단위로 다시 쓴다. 이걸 생략하면
원본 자동자막이 그대로 들어가 중복·오인식이 화면에 노출된다 — 생략하지 말 것.
- 시간은 구간 시작 기준 상대 초 (start=0.0이 구간 시작), 마지막 이벤트는 구간 길이를 넘지 않게
- 한 이벤트는 1
2줄 분량, 25초, 실제 말 타이밍과 대략 일치하게 (transcript_timestamped.txt 참조) - 음성인식 오류 교정, 간투사("어", "음") 제거
highlights.json 스키마:
[
{
"index": 1,
"start": 1065.1,
"end": 1096.5,
"title": "스피커 임베딩이 목소리를 결정한다",
"hook": "AI가 목소리를 배우는 법",
"reason": "화자 임베딩 개념이 독립적으로 설명되고 결론까지 이어짐",
"subtitles": [
{"start": 0.0, "end": 3.5, "text": "화자 테이블에서 스피커 정보를 가져옵니다"},
{"start": 3.5, "end": 7.0, "text": "이걸 인풋 임베딩에 넣어주면"}
]
}
]
Phase 3.5: 프레임 검수 (Claude가 직접 수행)
생성된 각 쇼츠에서 프레임을 뽑아 Read 도구로 직접 눈으로 확인한다:
ffmpeg -y -ss 1 -i output/shorts/short_01.mp4 -frames:v 1 output/check_01_hook.png # 후크 구간
ffmpeg -y -ss 10 -i output/shorts/short_01.mp4 -frames:v 1 output/check_01_mid.png # 본편 중간
체크리스트:
- 화면이 좌우/상하로 잘리지 않았다 (코드·슬라이드 글자가 온전히 보임)
- 자막이 하단에 적정 크기로 표시되고 화면 밖으로 넘치지 않는다
- 후크 텍스트가 읽히고, 핵심 콘텐츠를 가리지 않는다
- 검은 화면/빈 프레임이 없다
문제 발견 시 원인별로 조치한 뒤 해당 쇼츠만 재실행한다:
- 자막이 길거나 겹침 → highlights.json의 subtitles를 더 짧게 재작성
- 후크가 콘텐츠를 가림 → hook 문구 단축 또는 config의 hook_duration 축소
- 화면 잘림 →
--layout fit_blur확인 (crop은 토킹헤드 전용)
영상 레이아웃 선택 (중요)
원본이 16:9 가로 영상일 때 세로(9:16)로 바꾸는 방식. 원본 콘텐츠를 보고 고른다.
| layout | 동작 | 언제 쓰나 |
|---|---|---|
fit_blur (기본) |
원본 전체를 폭에 맞춰 넣고 위·아래를 블러 배경으로 채움. 아무것도 잘리지 않음 | 화면 녹화, 코드, 슬라이드/강의, 발표, 게임, 표·차트 등 가장자리 정보가 중요한 모든 경우 |
crop |
가운데를 꽉 채우고 좌우를 잘라냄 | 인물이 화면 중앙에 있는 토킹헤드·인터뷰·브이로그 전용 |
fit |
단색(검정) 레터박스 | 배경을 깔끔하게 두고 싶을 때 |
확신이 없으면 fit_blur. 강의·코드·화면 녹화를 crop으로 처리하면 좌우가 잘려 못 읽게 된다.
주의 — 원본에 자막이 구워진 영상(방송·예능 클립, 밈 영상 등): 인물 중심이라도 crop을 쓰면
화면 전체 폭에 깔린 원본 자막과 B롤이 좌우로 잘린다. 이런 영상은 fit_blur를 쓴다.
또한 원본 자막 위에 스킬 자막이 겹쳐 이중 자막이 되므로, 이 경우 highlights.json의
subtitles를 빈 배열([])로 두어 스킬 자막을 생략하고 후크만 얹는 것을 권장한다.
판단이 어려우면 Phase 3 전에 구간 프레임을 한 장 뽑아 원본 자막 유무를 눈으로 확인한다.
전자동 모드 (빠르지만 품질 타협)
Claude 큐레이션 없이 규칙 스코어만으로 한 번에 생성한다. 제목·자막이 자동자막 그대로라 인식 오류와 어색한 구간 선택이 남는다. 데모/시험용으로만 권장.
python3 scripts/run_pipeline.py --url "$URL" --count 10 --lang ko --layout fit_blur
카드뉴스 모드 (이미지 세트 + 카드 쇼츠 영상)
"카드뉴스", "개념 카드", "카드로 정리" 같은 요청이면 클립 대신 내용 요약 카드를 만든다.
디자인 시스템(다크 네이비 + 옐로 액센트, 1080x1920)은 scripts/card_news.py에 고정되어 있고,
Claude는 내용(cards.json)만 작성한다. 외부 API 불필요 (Pillow + ffmpeg).
절차:
# 1) 자막 추출 (이미 있으면 생략)
python3 scripts/run_pipeline.py --url "$URL" --candidates-only --skip-setup
# 2) [Claude] transcript를 읽고 output/cards.json 작성 (아래 스키마)
# 3) 카드 렌더링 (+ 옵션: 카드 쇼츠 영상)
python3 scripts/card_news.py --cards output/cards.json --output output/cards/ \
--video output/cards/cards_short.mp4 --seconds 4
# 3-1) 나레이션 포함 버전 (권장): AI 성우가 각 카드의 narration을 읽어주고,
# 카드 길이가 나레이션 길이에 자동으로 맞춰진다.
# 필요: pip install edge-tts --break-system-packages (무료, 인터넷 필수)
python3 scripts/card_news.py --cards output/cards.json --output output/cards/ \
--video output/cards/cards_short.mp4 --tts
# 4) [Claude] 생성된 PNG를 Read로 열어 검수 (텍스트 넘침/겹침/오탈자)
cards.json 스키마와 작성 규칙:
{
"series_label": "Qwen-TTS 파인튜닝 · 핵심 개념",
"footer": "전체 강의는 채널에서 ▶",
"cards": [
{
"index": 1,
"title_lines": ["로스는", "낮을수록 좋다?"],
"punch": "오해입니다",
"points": [
{"label": "실전 기준", "body": "여러 번 학습 결과, 로스 12~11 구간이\n가장 안정적인 품질"},
{"label": "예외 사례", "body": "7까지 내려가도 문제없던 경우 있음"},
{"label": "진짜 변수", "body": "로스보다 에폭 수가 중요"}
],
"narration": "로스는 낮을수록 좋다고 생각하기 쉬운데요, 사실은 오해입니다. 실제로 여러 번 학습해 보면 로스 12에서 11 구간이 가장 안정적이었고, 로스 숫자보다는 에폭 수가 훨씬 중요합니다."
}
]
}
- 카드 1장 = 개념 1개. 보통 3~5장 세트
title_lines: 질문/도발형 문구, 최대 2줄, 줄당 8자 내외 (길면 자동 축소되지만 짧게 쓰는 게 예쁘다)punch: 반전/답변 한 마디, 8자 이내 (예: "오해입니다", "비밀은 이것")points: 2~3개.label6자 이내,body2줄 이내(줄당 ~22자, 넘치면 자동 줄바꿈·말줄임)narration:--tts사용 시 AI 성우가 읽는 문장. **구어체 23문장(1020초 분량)**으로 카드 내용을 자연스럽게 풀어 쓴다. 카드 표시 시간이 이 길이에 자동으로 맞춰진다. 생략하면 제목+펀치를 그대로 읽는다 (어색하므로 꼭 작성 권장)- 영상 대본의 음성인식 오류는 문맥으로 교정해서 쓴다
- 렌더러가 출력한 "경고"(폰트 축소/잘림)가 있으면 해당 카드 문구를 줄여 다시 렌더링한다
핵심 제약사항
- 디스크 절약 최우선: torch, openai-whisper 등 대용량 패키지 설치 금지
- 의존성: ffmpeg + yt-dlp만 사용 (외부 API 없음)
- 자막: YouTube 자체 제공 자막 (수동 우선, 없으면 자동생성)
- 봇 감지 우회: 컨테이너 환경 자동 감지, 쿠키/UA/딜레이 조건부 적용
- 한글 지원: 폰트 자동 탐색 + 설치, 자막/후크 한글 렌더링
환경변수 (모두 선택사항)
YT_COOKIE_BROWSER: 쿠키 브라우저 (기본: chrome, 컨테이너에서는 자동 비활성)YT_PROXY: 프록시 URL (예: socks5://127.0.0.1:1080)CLAUDE_CONTAINER: 컨테이너 환경 강제 (1로 설정)
개별 Phase 건너뛰기
이미 실행한 Phase가 있으면 건너뛸 수 있다:
python3 scripts/run_pipeline.py --url "$URL" --candidates-only --skip-setup # 환경 설정 생략
python3 scripts/run_pipeline.py --url "$URL" --candidates-only --skip-setup --skip-subtitles # 자막도 이미 있음
참고
- 상세 레퍼런스: reference.md
- 설정 템플릿: templates/config_template.yaml