유튜브 자막
유튜브 동영상에서 자막(캡션)을 다운로드합니다. 수동으로 생성된 자막과 자동 생성된 자막 모두 작동합니다. API 키나 브라우저가 필요 없습니다 — 유튜브의 InnerTube API를 직접 사용하며, 유튜브가 직접 API 경로를 차단할 때는 자동으로 yt-dlp로 대체합니다.
첫 실행 시 동영상 메타데이터와 표지 이미지를 가져오고, 빠른 재포맷을 위해 원시 데이터를 캐시합니다.
스크립트 디렉터리
스크립트는 scripts/ 하위 디렉터리에 있습니다. {baseDir} = 이 SKILL.md의 디렉터리 경로입니다. ${BUN_X} 런타임 해결: bun이 설치되어 있으면 → bun; npx가 사용 가능하면 → npx -y bun; 그렇지 않으면 bun 설치를 제안합니다. {baseDir}와 ${BUN_X}를 실제 값으로 교체하세요.
| 스크립트 | 목적 |
|---|---|
scripts/main.ts | 자막 다운로드 CLI |
사용법
# Default: markdown with timestamps (English)
${BUN_X} {baseDir}/scripts/main.ts <youtube-url-or-id>
# Specify languages (priority order)
${BUN_X} {baseDir}/scripts/main.ts <url> --languages zh,en,ja
# Without timestamps
${BUN_X} {baseDir}/scripts/main.ts <url> --no-timestamps
# With chapter segmentation
${BUN_X} {baseDir}/scripts/main.ts <url> --chapters
# With speaker identification (requires AI post-processing)
${BUN_X} {baseDir}/scripts/main.ts <url> --speakers
# SRT subtitle file
${BUN_X} {baseDir}/scripts/main.ts <url> --format srt
# Translate transcript
${BUN_X} {baseDir}/scripts/main.ts <url> --translate zh-Hans
# List available transcripts
${BUN_X} {baseDir}/scripts/main.ts <url> --list
# Force re-fetch (ignore cache)
${BUN_X} {baseDir}/scripts/main.ts <url> --refresh
옵션
| 옵션 | 설명 | 기본값 |
|---|---|---|
<url-or-id> | 유튜브 URL 또는 동영상 ID (여러 개 허용) | 필수 |
--languages <codes> | 쉼표로 구분된 언어 코드, 우선 순위 순서 | en |
--format <fmt> | 출력 형식: text, srt | text |
--translate <code> | 지정된 언어 코드로 번역 | |
--list | 다운로드 대신 사용 가능한 자막 목록 표시 | |
--timestamps | 문단마다 [HH:MM:SS → HH:MM:SS] 타임스탬프 포함 | 켜짐 |
--no-timestamps | 타임스탬프 비활성화 | |
--chapters | 동영상 설명에서 챕터 분할 | |
--speakers | 화자 식별을 위한 메타데이터가 포함된 원시 자막 | |
--exclude-generated | 자동 생성된 자막 제외 | |
--exclude-manually-created | 수동 생성된 자막 제외 | |
--refresh | 강제로 다시 가져오기, 캐시된 데이터 무시 | |
-o, --output <path> | 특정 파일 경로에 저장 | 자동 생성 |
--output-dir <dir> | 기본 출력 디렉터리 | youtube-transcript |
선택적 환경 변수
| 변수 | 설명 |
|---|---|
YOUTUBE_TRANSCRIPT_COOKIES_FROM_BROWSER | 대체 시 yt-dlp --cookies-from-browser에 전달됩니다, 예: chrome, safari, firefox, 또는 chrome:Profile 1 |
입력 형식
동영상 입력으로 다음 중 하나를 허용합니다:
- 전체 URL:
https://www.youtube.com/watch?v=dQw4w9WgXcQ - 짧은 URL:
https://youtu.be/dQw4w9WgXcQ - 임베드 URL:
https://www.youtube.com/embed/dQw4w9WgXcQ - 쇼츠 URL:
https://www.youtube.com/shorts/dQw4w9WgXcQ - 동영상 ID:
dQw4w9WgXcQ
출력 형식
| 형식 | 확장자 | 설명 |
|---|---|---|
text | .md | 프론트매터(description 포함), 제목 헤딩, 요약, 선택적 목차/표지/타임스탬프/챕터/화자 정보가 포함된 마크다운 |
srt | .srt | 동영상 플레이어용 SubRip 자막 형식 |
출력 디렉터리
youtube-transcript/
├── .index.json # Video ID → directory path mapping (for cache lookup)
└── {channel-slug}/{title-full-slug}/
├── meta.json # Video metadata (title, channel, description, duration, chapters, etc.)
├── transcript-raw.json # Raw transcript snippets from YouTube API (cached)
├── transcript-sentences.json # Sentence-segmented transcript (split by punctuation, merged across snippets)
├── imgs/
│ └── cover.jpg # Video thumbnail
├── transcript.md # Markdown transcript (generated from sentences)
└── transcript.srt # SRT subtitle (generated from raw snippets, if --format srt)
{channel-slug}: 케밥 케이스의 채널 이름{title-full-slug}: 케밥 케이스의 전체 동영상 제목
--list 모드는 표준 출력으로만 출력합니다 (파일 저장 안 함).
캐싱
첫 가져오기 시 스크립트는 다음을 저장합니다:
meta.json— 동영상 메타데이터, 챕터, 표지 이미지 경로, 언어 정보transcript-raw.json— 유튜브 API의 원시 자막 조각들 ({ text, start, duration }[])transcript-sentences.json— 문장으로 분할된 자막 ({ text, start: "HH:mm:ss", end: "HH:mm:ss" }[]), 문장 종결 구두점(.?!…。?!등)으로 분할, 타임스탬프는 문자 길이에 비례하여 할당, CJK 인식 텍스트 병합imgs/cover.jpg— 동영상 썸네일
동일한 동영상에 대한 이후 실행은 캐시된 데이터를 사용합니다 (네트워크 호출 없음). 강제로 다시 가져오려면 --refresh를 사용하세요. 다른 언어가 요청되면 캐시가 자동으로 새로 고쳐집니다.
유튜브가 직접 InnerTube 경로에서 안티봇/차단 응답을 반환하면 스크립트는 대체 클라이언트 ID로 재시도한 후, 가능한 경우 yt-dlp로 대체합니다. 대체가 필요하지만 yt-dlp를 사용할 수 없는 경우, 에이전트는 사용자에게 설치 결정을 미루지 않고 직접 yt-dlp를 사용 가능하게 만드는 방법을 결정해야 합니다.
SRT 출력(--format srt)은 transcript-raw.json에서 생성됩니다. 텍스트/마크다운 출력은 자연스러운 문장 경계를 위해 transcript-sentences.json을 사용합니다.
워크플로우
사용자가 유튜브 URL을 제공하고 자막을 원할 때:
- 사용자가 언어를 지정하지 않은 경우 먼저
--list로 실행하여 사용 가능한 옵션을 표시합니다. - 스크립트를 실행할 때는 항상 URL을 작은따옴표로 묶으십시오 — zsh는
?를 글로브 와일드카드로 처리하므로, 따옴표가 없는 유튜브 URL은 "no matches found" 오류를 발생시킵니다:'https://www.youtube.com/watch?v=ID'를 사용하세요. - 기본값: 가장 풍부한 출력을 위해
--chapters --speakers로 실행합니다 (챕터 + 화자 식별). - 스크립트는 캐시된 데이터 + 출력 파일을 자동 저장하고 파일 경로를 출력합니다.
--speakers모드의 경우: 스크립트가 원시 파일을 저장한 후, 아래의 화자 식별 워크플로우에 따라 화자 라벨로 후처리하세요.
사용자가 표지 이미지나 메타데이터만 원하는 경우, 어떤 옵션으로 스크립트를 실행해도 meta.json과 imgs/cover.jpg가 캐시됩니다.
동일한 동영상을 재포맷할 때(예: 먼저 텍스트, 그 다음 SRT), 캐시된 데이터가 재사용됩니다 — 다시 가져올 필요 없습니다.
챕터 및 화자 워크플로우
챕터 (--chapters)
스크립트는 동영상 설명에서 챕터 타임스탬프(예: 0:00 Introduction)를 파싱하여 자막을 챕터 경계로 분할하고, 조각들을 읽기 쉬운 문단으로 그룹화하여 목차와 함께 .md로 저장합니다. 추가 처리가 필요하지 않습니다.
설명에 챕터 타임스탬프가 없는 경우, 자막은 챕터 제목 없이 그룹화된 문단으로 출력됩니다.
화자 식별 (--speakers)
화자 식별에는 AI 처리가 필요합니다. 스크립트는 다음을 포함하는 원시 .md 파일을 출력합니다:
- 동영상 메타데이터(제목, 채널, 날짜, 표지, 설명, 언어)가 포함된 YAML 프론트매터
- 동영상 설명 (화자 이름 추출용)
- 설명에서 가져온 챕터 목록 (가능한 경우)
- SRT 형식의 원시 자막 (미리 계산된 시작/종료 타임스탬프, 토큰 효율적)
스크립트가 원시 파일을 저장한 후, 하위 에이전트(비용 효율성을 위해 Sonnet과 같은 저렴한 모델 사용)를 생성하여 화자 식별을 처리하세요:
- 저장된
.md파일을 읽습니다. {baseDir}/prompts/speaker-transcript.md에 있는 프롬프트 템플릿을 읽습니다.- 프롬프트에 따라 원시 자막을 처리합니다:
- 동영상 메타데이터를 사용하여 화자 식별 (제목 → 게스트, 채널 → 호스트, 설명 → 이름)
- 대화 흐름, 질문-답변 패턴, 맥락 단서에서 화자 전환 감지
- 챕터로 분할 (가능한 경우 설명 챕터 사용, 그렇지 않으면 주제 전환에서 생성)
**화자 이름:**라벨, 문단 그룹화(2-4문장),[HH:MM:SS → HH:MM:SS]타임스탬프로 포맷
- 처리된 자막으로
.md파일을 덮어씁니다 (YAML 프론트매터 유지)
--speakers가 사용되면 --chapters가 암시됩니다 — 처리된 출력에는 항상 챕터 분할이 포함됩니다.
오류 사례
| 오류 | 의미 |
|---|---|
| 자막 비활성화됨 | 동영상에 캡션이 전혀 없습니다 |
| 자막을 찾을 수 없음 | 요청한 언어를 사용할 수 없습니다 |
| 동영상을 사용할 수 없음 | 동영상이 삭제되었거나 비공개이거나 지역 제한이 있습니다 |
| IP 차단됨 | 요청이 너무 많습니다. 나중에 다시 시도하세요 |
| 연령 제한됨 | 연령 확인을 위해 로그인이 필요합니다 |
| 봇 감지됨 | 스크립트가 대체 클라이언트를 재시도한 후 yt-dlp를 시도합니다; 대체 도구가 누락된 경우 에이전트가 자체적으로 해결해야 하며, 그래도 실패하면 YOUTUBE_TRANSCRIPT_COOKIES_FROM_BROWSER=safari(또는 사용하는 브라우저)를 시도하세요 |


