장편 영상 → 한국어 큐레이션 요약 영상 파이프라인
개요 (한국어): 35분짜리 YouTube 키노트 같은 장편 영상을 4~5분짜리 한국어 큐레이션 요약 영상으로 만든 노하우. ReClip(yt-dlp)으로 받기 → 자막 추출 → 의미 단위로 mini-clip 분리 → edge-tts 한국어 내레이션 → ffmpeg 합성까지의 전 과정. 2026-05-20 Google I/O 2026 keynote 작업에서 도출.
1. 파이프라인 전체 그림
YouTube URL
│
▼ [yt-dlp]
원본 mp4 + 자동 자막 VTT
│
▼ [Claude 정독]
전체 흐름 파악 → 핵심 섹션 N개 선정
│
▼ [Python: VTT cue 검색]
각 핵심 자막 줄 → 영문 앵커 → 원본 timestamp
│
▼ [Claude 작성]
화면 자막(요약 헤드라인) + 내레이션 텍스트(친절 설명조) 분리 작성
│
▼ [edge-tts]
mini-clip마다 한국어 narration mp3 생성 + duration 측정
│
▼ [ffmpeg]
mini-clip cut (clip_dur = TTS_dur + 0.8s) + 무음 패딩 + TTS 합성
│
▼ [ffmpeg concat + subtitles filter]
한 본으로 합치고 자막 burn-in (Malgun Gothic)
│
▼
최종 mp4 (영상 + 한국어 내레이션 + 헤드라인 자막)
2. 단계별 핵심 노하우
2.1 영상·자막 받기 (yt-dlp)
ReClip 같은 Flask 래퍼를 Windows에서 직접 실행할 때 함정:
app.py가subprocess.run(["yt-dlp", ...])로 시스템 PATH에서 yt-dlp를 찾는다.- venv 안에만 yt-dlp가 있으면
[WinError 2] 지정된 파일을 찾을 수 없습니다가 발생. - 해결: 실행 전
$env:PATH = "venv\Scripts;$env:PATH"로 venv\Scripts 를 PATH 앞에 박는다. Linux/Mac의source venv/bin/activate가 PATH를 갱신해주는 것을 Windows에서는 수동으로 해야 한다.
자막만 추출하는 명령:
yt-dlp --skip-download --write-auto-subs --sub-langs "en,ko" `
--sub-format vtt -o "subs\%(id)s.%(ext)s" "<URL>"- YouTube 자동자막은 다국어가 다 옵션에 보이지만 Rate Limit(HTTP 429)에 자주 걸린다. 한국어는 1차 실패하면 그냥 영어만 받고 자체 번역(또는 친절 설명조 한국어 재작성)으로 우회.
- vtt에는 단어 단위 타이밍
<00:00:01.234>가 섞여 있다.<[^>]+>정규식으로 제거.
2.2 VTT 파싱 (timestamp 보존)
VTT cue를 (start, end, text) 튜플 리스트로 만들어두면, 이후 모든 단계에서 timestamp 검색이 쉬워진다. YouTube auto-VTT는 연속 cue가 같은 텍스트로 누적되는 특성이 있어, 연속 중복은 한 cue로 머지하는 게 좋다.
cues.json 같은 중간 산출물로 저장해두면 디버깅·실험·재실행 비용이 확 준다.
2.3 의미 단위 분할 — “자막 1줄 = 1컷” 원칙
처음에는 섹션 단위(30~40초 1개 cut)로 자르고 자막을 그 위에 흘렸는데, 그 30초 안에서 화면이 여러 슬라이드로 바뀌어 자막이 말하는 내용과 화면이 안 맞는다.
정답: 자막을 5~8초 짜리 줄 단위로 잘게 쪼개고, 각 자막 줄에 해당하는 원본의 그 발화 순간을 검색해서 그 시점에서 짧은 mini-clip(=clip_dur)을 잘라낸다. 그러면 자막이 말하는 그 화면만 정확히 나온다.
검색 키는 영문 앵커 문구:
- 큐레이션 자막을 한국어로 작성
- 각 자막에 대해 transcript에서 그 내용을 가장 잘 표현하는 영문 어구 1개를 골라 앵커로 지정
- 앵커 문구를 VTT cue에서 lower-case substring 매칭으로 검색 → start time
prev변수로 직전 매칭 이후만 검색해서 시간 단조 증가 보장
시간 역행 사고: 동일 키워드가 영상 후반에도 등장하면 (예: “coming this fall”) 의도와 다른 시점이 잡힌다. 검증 단계에서 clips[i].src >= clips[i-1].src 를 확인하고, 깨지면 더 고유한 앵커(예: “two designs of a bigger collection”)로 교체하거나 큐레이션 순서를 transcript 시간 순으로 재정렬.
2.4 자막 ≠ 내레이션 (분리 설계)
자막을 그대로 TTS로 읽히면 짧고 딱딱하다. 처음엔 그렇게 했지만 결과물이 어색했다.
분리 원칙:
- 자막 (화면 표시): 짧은 헤드라인 — “Maps, 10년 만의 최대 업그레이드”
- 내레이션 (음성): 친절한 설명조 — “구글 맵스는 십년 만에 가장 큰 업그레이드를 단행했습니다.”
자막은 “한 눈에 보이는 요약”이고 내레이션은 “친절한 설명”이라 역할이 다르다.
2.5 한국어 TTS — edge-tts
선택 이유:
- 무료, API 키 불필요, 인터넷만 있으면 됨
- 한국어 신경망 음성 3종 (
ko-KR-HyunsuMultilingualNeural남성·다국어,ko-KR-InJoonNeural남성,ko-KR-SunHiNeural여성) - 합성 품질이 클라우드 TTS급
asyncio.gather로 46개 batch 동시 호출 → 1초 미만
설치:
pip install edge-tts호출 예:
import edge_tts, asyncio
async def gen():
c = edge_tts.Communicate("안녕하세요", "ko-KR-HyunsuMultilingualNeural", rate="+6%")
await c.save("out.mp3")
asyncio.run(gen())2.6 영어·숫자 → 한글 표기 (TTS 정확 발음의 결정타)
TTS가 영어 단어를 영어 발음으로 읽으면 한국어 흐름에서 어색하다. 내레이션 텍스트에서 영어와 숫자를 모두 한글로 풀어 쓰면 TTS가 한국어 음운으로 자연스럽게 읽는다.
| 원어/숫자 | 내레이션 표기 |
|---|---|
| Gemini | 제미나이 |
| YouTube | 유튜브 |
| Anti-Gravity 2.0 | 안티 그래비티 이점영 |
| Doom | 둠 |
| Door Dash | 도어 대시 |
| Cloud VM | 클라우드 가상머신 |
| MCP | 엠씨피 |
| iOS | 아이오에스 |
| Halo | 헤일로 |
| AGI | 에이지아이 |
| Isomorphic Labs | 아이소모픽 랩스 |
| Pro·Ultra | 프로, 울트라 |
| 10년 | 십년 |
| 30분 | 삼십분 |
| 24시간 | 이십사시간 |
| 2.0 | 이점영 |
표기 규칙은 붙여쓰기가 자연스럽다 (“십 년” 보다 “십년”). 띄어쓰기를 넣으면 TTS가 약간 끊어 읽는다.
자막은 원어/숫자 그대로 유지 — 시각적 가독성이 더 중요하고, 내레이션이 이를 풀어 발음해주므로 충돌 없음.
2.7 ffmpeg — clip cut + TTS 합성
mini-clip마다 영상은 원본 timestamp에서 자르고, 오디오는 원본을 버리고 TTS를 입힌다. 각 clip의 길이는 그 자막의 TTS 길이에 동적으로 맞춘다:
clip_dur = max(MIN_DUR, tts_dur + PAD_LEAD + PAD_TAIL)
# PAD_LEAD=0.30 → TTS가 시작되기 전 살짝 화면 노출
# PAD_TAIL=0.50 → TTS 끝나고 화면이 살짝 더 머무름ffmpeg 한 줄로 비디오는 무음 cut + TTS 오디오를 adelay 로 lead 만큼 미루고 apad 로 clip 끝까지 padding:
ffmpeg -ss SRC_START -t CLIP_DUR -i src.mp4 \
-i tts.mp3 \
-filter_complex "[0:v]scale=1280:-2,fps=30,format=yuv420p[v];\
[1:a]adelay=300|300,apad=whole_dur=CLIP_DUR[a]" \
-map "[v]" -map "[a]" \
-c:v libx264 -preset veryfast -crf 22 \
-c:a aac -b:a 128k -shortest out.mp4각 clip을 동일 코덱·해상도·fps로 통일해놓으면 다음 단계 concat 이 -c copy 로 가능해 빠르다.
2.8 concat + 자막 burn-in
# concat
ffmpeg -f concat -safe 0 -i list.txt -c:v copy -c:a aac -b:a 128k concat.mp4
# burn-in (Windows: cwd 이동해서 상대경로 — subtitles 필터가 콜론을 옵션 구분자로 오인)
cd outdir
ffmpeg -i concat.mp4 \
-vf "subtitles=subs.srt:force_style='FontName=Malgun Gothic,Fontsize=20,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=2,Shadow=0,Alignment=2,MarginV=48'" \
-c:v libx264 -preset veryfast -crf 22 -c:a copy final.mp4Windows ffmpeg subtitles 필터의 함정: D:/... 경로의 : 을 ffmpeg가 옵션 구분자로 오해. cwd를 outdir로 옮기고 subtitles=subs.srt 처럼 상대 경로로 호출하면 안전.
자막 SRT 텍스트 인코딩은 UTF-8, 폰트는 Malgun Gothic (한국어 가독성). 흰 글씨 + 검은 외곽선이 어떤 화면 위에서도 잘 읽힌다.
2.9 1.2배속 후처리
원본 영상 + 오디오를 같이 가속:
ffmpeg -i in.mp4 \
-filter_complex "[0:v]setpts=PTS/1.2[v];[0:a]atempo=1.2[a]" \
-map "[v]" -map "[a]" \
-c:v libx264 -preset veryfast -crf 22 -c:a aac -b:a 128k out.mp4atempo 는 0.5~2.0 범위. 그 이상은 atempo=2.0,atempo=1.2 식으로 체인.
3. Python stdout 인코딩 (Windows-only 함정)
Windows cp949 콘솔에서 한국어 + emdash(—) 같은 유니코드 문자를 print 하면 UnicodeEncodeError 가 자주 난다. 스크립트 맨 위에 박는 한 줄:
import sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')4. 산출물 디렉토리 컨벤션 (반복 작업용)
D:\claude\reclip\
├── downloads\<job_id>.mp4 # 원본 다운로드
├── subs\
│ ├── <video_id>.en.vtt # 원본 자막
│ ├── <video_id>.en.txt # 깨끗한 transcript (Claude가 읽기용)
│ └── cues.json # 파싱된 (start, end, text) 큐
├── highlights<N>\ # 반복 시도마다 N 증가
│ ├── tts\tts_NN.mp3 # mini-clip별 TTS
│ ├── m00~mNN.mp4 # mini-clip
│ ├── concat.mp4 # 자막 없는 본
│ ├── subs.srt # 사이드카 자막
│ └── highlight_vN.mp4 # 최종본
highlights<N> 으로 폴더를 분리해두면 v1→v2→v3 변형을 비교하기 쉽다.
5. 빌드 스크립트 템플릿 위치
C:\Users\USER\.claude\jobs\<job-id>\build_vN.py 에 빌드 스크립트를 둔다 (PLAN 리스트 = 자막 + 내레이션 + 앵커).
한 PLAN 리스트가 모든 정보 보유 — 자막 텍스트, 내레이션 텍스트, 영문 앵커. 코드는 거의 변하지 않고 텍스트만 바뀐다. v1~v5는 코드 동일, PLAN만 진화.
6. 검증 절차 (각 빌드 후)
- 순서 검증:
clips[i].src >= clips[i-1].src단조 증가 확인 → 깨지면 앵커 교체 - 샘플 프레임 추출: 서로 다른 섹션 3~4 지점에서 frame PNG 1장씩 뽑아 자막↔화면 매칭 시각 확인
ffmpeg -ss <t> -i final.mp4 -frames:v 1 sample.png - ffprobe duration: 총 길이가 의도와 일치 확인
- 첫 30초 청취: 내레이션이 자연스럽게 들리는지
7. 향후 확장 가능성
- Qwen3-TTS voice clone: ComfyUI-Qwen3-TTS의
Qwen3VoiceClone노드 + Base 모델로 reference audio 기반 클로닝 가능. 시도 시 ComfyUI 서버 띄워야 함 (GPU 필요). 한국어 reference audio 30초 정도가 적절. - VoiceDesign: 자연어 description으로 음성 디자인. CustomVoice 모델 다운로드 시 9종 preset 사용 가능 (Vivian, Ryan 등).
- LLM 기반 자동 큐레이션: 이번엔 Claude(나)가 transcript를 직접 정독해서 큐레이션을 손으로 짰지만, 외부 LLM API에 transcript와 길이 목표를 던져 큐레이션 JSON을 받아오게 자동화 가능. 단 영어→한글 표기 변환은 보정 룰을 별도로 박아야 안정적.
8. 이번 작업의 최종 산출물 (2026-05-20)
| 버전 | 특징 | 길이 | 경로 |
|---|---|---|---|
| v3 | 자막=TTS, 영어/숫자 원문 | 3:57 | D:\claude\reclip\highlights4\highlight_v3.mp4 |
| v4 | 자막/내레이션 분리, 영어→한글 | 5:13 | D:\claude\reclip\highlights5\highlight_v4.mp4 |
| v5 | + 숫자→한글 | 5:13 | D:\claude\reclip\highlights6\highlight_v5.mp4 |
| v5 fast 1.2x | v5 가속 | 4:21 | D:\claude\reclip\highlights6\highlight_v5_fast12.mp4 |
| v6 (여성) | SunHi voice | 5:09 | D:\claude\reclip\highlights7\highlight_v6_female.mp4 |
| v6 fast 1.2x | v6 가속 | 4:18 | D:\claude\reclip\highlights7\highlight_v6_female_fast12.mp4 |
원본 영상: Google I/O 2026 keynote 발췌 35분 (The Verge, OMhKgQmeMhI).
