/video-subtitle — 비디오 자막 도구
로컬 Whisper 모델을 사용하여 비디오 오디오를 SRT 자막으로 변환한 후, 비디오에 자막을 입힙니다.
사용자 호출
사용자가 /video-subtitle을 입력하면 이 스킬을 실행합니다.
사전 요구사항
이 스킬에는 두 가지 도구가 필요합니다. 진행하기 전에 사용 가능 여부를 확인하세요.
1. mlx-whisper (Apple Silicon) 또는 openai-whisper
사용 가능한 것을 감지합니다:
which mlx_whisper whisper 2>/dev/null
둘 다 없을 경우, 사용자에게 다음 중 하나를 설치하도록 안내합니다:
- Apple Silicon Mac (권장):
pip install mlx-whisper - 모든 플랫폼:
pip install openai-whisper
2. libass를 지원하는 ffmpeg
libass 지원 여부 확인:
ffmpeg -filters 2>&1 | grep subtitles
출력이 없으면(libass 누락) subtitles 필터를 사용할 수 없습니다:
- macOS:
brew install ffmpeg-full(keg-only,/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg) - Linux:
sudo apt install ffmpeg(보통 libass 포함) - 대체: 사용자가 libass가 없는 일반 ffmpeg를 사용 중이라면, 자막을 굽는 대신 SRT를 자막 트랙으로 소프트 임베딩합니다.
Whisper 모델 선택
캐시된 모델 확인:
ls ~/.cache/huggingface/hub/ 2>/dev/null | grep whisper
가장 큰 사용 가능한 모델을 선호합니다. 캐시된 것이 없으면 다음을 사용합니다:
- mlx-whisper:
mlx-community/whisper-large-v3-mlx - openai-whisper:
large-v3
모드
/video-subtitle path/to/video.mov→ 전체 파이프라인 (변환 + 보정 + 굽기)/video-subtitle srt path/to/video.mov→ SRT만 (변환, 굽지 않음)/video-subtitle burn path/to/video.mov path/to/sub.srt→ 굽기만 (기존 SRT 사용)/video-subtitle(인자 없음) → 대화형, 비디오 경로 요청
전체 파이프라인
1단계: 비디오 확인
경로가 제공되지 않으면 AskUserQuestion으로 요청합니다. 파일이 존재하는지 확인하고 메타데이터를 읽습니다:
ffmpeg -i "<video_path>" 2>&1 | grep -E "Duration|Stream.*(Video|Audio)"
지속 시간, 해상도, 파일 크기를 사용자에게 보고합니다.
2단계: 오디오 추출
ffmpeg -i "<video_path>" -vn -acodec pcm_s16le -ar 16000 -ac 1 "<output_dir>/<basename>_audio.wav" -y
16kHz 모노 WAV는 Whisper에 최적의 입력 형식입니다.
3단계: 언어 확인
AskUserQuestion으로 사용자에게 물어봅니다. 일반적인 옵션:
| 언어 | 플래그 |
|---|---|
| 한국어 | --language ko |
| 영어 | --language en |
| 일본어 | --language ja |
| 자동 감지 | --language 생략 |
4단계: 변환
mlx-whisper:
mlx_whisper --model "<model>" --language <lang> --output-format srt --output-dir "<output_dir>" "<audio_wav>"
openai-whisper:
whisper "<audio_wav>" --model large-v3 --language <lang> --output_format srt --output_dir "<output_dir>"
사용자에게 알립니다: "변환 중입니다. 잠시 기다려 주세요..."
5단계: SRT 정리
생성된 SRT를 읽고 다음을 수정합니다:
- 환각 제거: 지속 시간이 0인 타임스탬프(예:
00:00:57,640 --> 00:00:57,640)나 빈 텍스트가 있는 항목 삭제 - 고유명사 수정: Whisper가 브랜드명이나 기술 용어를 잘못 인식하는 경우가 많음 — 대화 맥락을 사용하여 수정
- 조각 병합: 의미적으로 연결된 0.5초 미만의 짧은 항목 결합
- 재번호 부여: 다음 Python 스니펫을 실행하여 모든 항목에 번호를 다시 매깁니다:
python3 -c "
import re
with open('<srt_path>', 'r') as f:
content = f.read()
blocks = re.split(r'\n\n+', content.strip())
out = []
for i, block in enumerate(blocks, 1):
lines = block.strip().split('\n')
if len(lines) >= 2:
lines[0] = str(i)
out.append('\n'.join(lines))
with open('<srt_path>', 'w') as f:
f.write('\n\n'.join(out) + '\n')
"
수정된 SRT를 사용자에게 보여주고 굽기 전에 추가 편집이 필요한지 묻습니다.
6단계: 자막 굽기
올바른 ffmpeg 바이너리를 결정합니다(일반 ffmpeg에 libass가 없으면 ffmpeg-full 선호):
FFMPEG="ffmpeg"
if ! ffmpeg -filters 2>&1 | grep -q subtitles; then
if [ -x /opt/homebrew/opt/ffmpeg-full/bin/ffmpeg ]; then
FFMPEG="/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg"
fi
fi
그런 다음 굽기:
$FFMPEG \
-i "<video_path>" \
-vf "subtitles=<srt_path>:force_style='FontSize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,MarginV=30'" \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-pix_fmt yuv420p \
"<output_dir>/<basename>_subtitled.mp4" -y
force_style 옵션:
| 매개변수 | 기본값 | 설명 |
|---|---|---|
| FontSize | 24 | 글꼴 크기 |
| PrimaryColour | &H00FFFFFF | 흰색 텍스트 |
| OutlineColour | &H00000000 | 검은색 윤곽선 |
| Outline | 2 | 윤곽선 두께 |
| MarginV | 30 | 아래쪽 여백 |
중요: SRT 파일 경로에 ffmpeg 필터 파서를 혼동시키는 특수 문자가 없어야 합니다. 필요한 경우 상대 경로나 심볼릭 링크를 사용하세요.
7단계: 완료
출력 파일 경로와 크기를 보고합니다. open <path>로 미리 보기를 제안합니다. 중간 파일인 _audio.wav를 삭제할 수 있음을 알립니다.
문제 해결
| 문제 | 원인 | 해결책 |
|---|---|---|
No such filter: 'subtitles' | ffmpeg에 libass 없음 | ffmpeg-full 사용 또는 libass 설치 |
No such filter: 'drawtext' | ffmpeg에 libfreetype 없음 | ffmpeg-full 사용 |
| 중복/빈 SRT 항목 | Whisper 환각 | 5단계에서 정리 |
| 한글이 네모로 표시됨 | CJK 글꼴 없음 | macOS: 자동으로 Apple SD Gothic Neo로 대체. Linux: apt install fonts-noto-cjk |
Error parsing filterchain | SRT 경로에 특수 문자 | 상대 경로 사용, 공백 피하기 |
품질 규칙
- 충실한 변환: 실제로 말한 내용만 변환 — 의미를 추가하거나 다시 쓰지 않음
- 고유명사 수정: 일반적인 Whisper 오인식을 수정하지만, 사용자와 확인
- 자막 리듬: 항목당 최대 두 줄, 줄당 최대 ~20자(한국어 기준)
- 정리: 완료 후 중간 파일(_audio.wav) 삭제를 사용자에게 알림